select-list-view.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. (function() {
  2. var $, SelectListView, TextEditorView, View, fuzzyFilter, _ref,
  3. __hasProp = {}.hasOwnProperty,
  4. __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
  5. _ref = require('space-pen'), $ = _ref.$, View = _ref.View;
  6. TextEditorView = require('./text-editor-view');
  7. fuzzyFilter = null;
  8. atom.themes.requireStylesheet(require.resolve('../stylesheets/select-list.less'));
  9. module.exports = SelectListView = (function(_super) {
  10. __extends(SelectListView, _super);
  11. function SelectListView() {
  12. return SelectListView.__super__.constructor.apply(this, arguments);
  13. }
  14. SelectListView.content = function() {
  15. return this.div({
  16. "class": 'select-list'
  17. }, (function(_this) {
  18. return function() {
  19. _this.subview('filterEditorView', new TextEditorView({
  20. mini: true
  21. }));
  22. _this.div({
  23. "class": 'error-message',
  24. outlet: 'error'
  25. });
  26. _this.div({
  27. "class": 'loading',
  28. outlet: 'loadingArea'
  29. }, function() {
  30. _this.span({
  31. "class": 'loading-message',
  32. outlet: 'loading'
  33. });
  34. return _this.span({
  35. "class": 'badge',
  36. outlet: 'loadingBadge'
  37. });
  38. });
  39. return _this.ol({
  40. "class": 'list-group',
  41. outlet: 'list'
  42. });
  43. };
  44. })(this));
  45. };
  46. SelectListView.prototype.maxItems = Infinity;
  47. SelectListView.prototype.scheduleTimeout = null;
  48. SelectListView.prototype.inputThrottle = 50;
  49. SelectListView.prototype.cancelling = false;
  50. /*
  51. Section: Construction
  52. */
  53. SelectListView.prototype.initialize = function() {
  54. this.filterEditorView.getModel().getBuffer().onDidChange((function(_this) {
  55. return function() {
  56. return _this.schedulePopulateList();
  57. };
  58. })(this));
  59. this.filterEditorView.on('blur', (function(_this) {
  60. return function(e) {
  61. if (!(_this.cancelling || !document.hasFocus())) {
  62. return _this.cancel();
  63. }
  64. };
  65. })(this));
  66. atom.commands.add(this.element, {
  67. 'core:move-up': (function(_this) {
  68. return function(event) {
  69. _this.selectPreviousItemView();
  70. return event.stopPropagation();
  71. };
  72. })(this),
  73. 'core:move-down': (function(_this) {
  74. return function(event) {
  75. _this.selectNextItemView();
  76. return event.stopPropagation();
  77. };
  78. })(this),
  79. 'core:move-to-top': (function(_this) {
  80. return function(event) {
  81. _this.selectItemView(_this.list.find('li:first'));
  82. _this.list.scrollToTop();
  83. return event.stopPropagation();
  84. };
  85. })(this),
  86. 'core:move-to-bottom': (function(_this) {
  87. return function(event) {
  88. _this.selectItemView(_this.list.find('li:last'));
  89. _this.list.scrollToBottom();
  90. return event.stopPropagation();
  91. };
  92. })(this),
  93. 'core:confirm': (function(_this) {
  94. return function(event) {
  95. _this.confirmSelection();
  96. return event.stopPropagation();
  97. };
  98. })(this),
  99. 'core:cancel': (function(_this) {
  100. return function(event) {
  101. _this.cancel();
  102. return event.stopPropagation();
  103. };
  104. })(this)
  105. });
  106. this.list.on('mousedown', (function(_this) {
  107. return function(_arg) {
  108. var target;
  109. target = _arg.target;
  110. if (target === _this.list[0]) {
  111. return false;
  112. }
  113. };
  114. })(this));
  115. this.list.on('mousedown', 'li', (function(_this) {
  116. return function(e) {
  117. _this.selectItemView($(e.target).closest('li'));
  118. e.preventDefault();
  119. return false;
  120. };
  121. })(this));
  122. return this.list.on('mouseup', 'li', (function(_this) {
  123. return function(e) {
  124. if ($(e.target).closest('li').hasClass('selected')) {
  125. _this.confirmSelection();
  126. }
  127. e.preventDefault();
  128. return false;
  129. };
  130. })(this));
  131. };
  132. /*
  133. Section: Methods that must be overridden
  134. */
  135. SelectListView.prototype.viewForItem = function(item) {
  136. throw new Error("Subclass must implement a viewForItem(item) method");
  137. };
  138. SelectListView.prototype.confirmed = function(item) {
  139. throw new Error("Subclass must implement a confirmed(item) method");
  140. };
  141. /*
  142. Section: Managing the list of items
  143. */
  144. SelectListView.prototype.setItems = function(items) {
  145. this.items = items != null ? items : [];
  146. this.populateList();
  147. return this.setLoading();
  148. };
  149. SelectListView.prototype.getSelectedItem = function() {
  150. return this.getSelectedItemView().data('select-list-item');
  151. };
  152. SelectListView.prototype.getFilterKey = function() {};
  153. SelectListView.prototype.getFilterQuery = function() {
  154. return this.filterEditorView.getText();
  155. };
  156. SelectListView.prototype.setMaxItems = function(maxItems) {
  157. this.maxItems = maxItems;
  158. };
  159. SelectListView.prototype.populateList = function() {
  160. var filterQuery, filteredItems, i, item, itemView, _i, _ref1;
  161. if (this.items == null) {
  162. return;
  163. }
  164. filterQuery = this.getFilterQuery();
  165. if (filterQuery.length) {
  166. if (fuzzyFilter == null) {
  167. fuzzyFilter = require('fuzzaldrin').filter;
  168. }
  169. filteredItems = fuzzyFilter(this.items, filterQuery, {
  170. key: this.getFilterKey()
  171. });
  172. } else {
  173. filteredItems = this.items;
  174. }
  175. this.list.empty();
  176. if (filteredItems.length) {
  177. this.setError(null);
  178. for (i = _i = 0, _ref1 = Math.min(filteredItems.length, this.maxItems); 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) {
  179. item = filteredItems[i];
  180. itemView = $(this.viewForItem(item));
  181. itemView.data('select-list-item', item);
  182. this.list.append(itemView);
  183. }
  184. return this.selectItemView(this.list.find('li:first'));
  185. } else {
  186. return this.setError(this.getEmptyMessage(this.items.length, filteredItems.length));
  187. }
  188. };
  189. /*
  190. Section: Messages to the user
  191. */
  192. SelectListView.prototype.setError = function(message) {
  193. if (message == null) {
  194. message = '';
  195. }
  196. if (message.length === 0) {
  197. return this.error.text('').hide();
  198. } else {
  199. this.setLoading();
  200. return this.error.text(message).show();
  201. }
  202. };
  203. SelectListView.prototype.setLoading = function(message) {
  204. if (message == null) {
  205. message = '';
  206. }
  207. if (message.length === 0) {
  208. this.loading.text("");
  209. this.loadingBadge.text("");
  210. return this.loadingArea.hide();
  211. } else {
  212. this.setError();
  213. this.loading.text(message);
  214. return this.loadingArea.show();
  215. }
  216. };
  217. SelectListView.prototype.getEmptyMessage = function(itemCount, filteredItemCount) {
  218. return 'No matches found';
  219. };
  220. /*
  221. Section: View Actions
  222. */
  223. SelectListView.prototype.cancel = function() {
  224. var filterEditorViewFocused;
  225. this.list.empty();
  226. this.cancelling = true;
  227. filterEditorViewFocused = this.filterEditorView.hasFocus();
  228. if (typeof this.cancelled === "function") {
  229. this.cancelled();
  230. }
  231. this.filterEditorView.setText('');
  232. if (filterEditorViewFocused) {
  233. this.restoreFocus();
  234. }
  235. this.cancelling = false;
  236. return clearTimeout(this.scheduleTimeout);
  237. };
  238. SelectListView.prototype.focusFilterEditor = function() {
  239. return this.filterEditorView.focus();
  240. };
  241. SelectListView.prototype.storeFocusedElement = function() {
  242. return this.previouslyFocusedElement = $(document.activeElement);
  243. };
  244. /*
  245. Section: Private
  246. */
  247. SelectListView.prototype.selectPreviousItemView = function() {
  248. var view;
  249. view = this.getSelectedItemView().prev();
  250. if (!view.length) {
  251. view = this.list.find('li:last');
  252. }
  253. return this.selectItemView(view);
  254. };
  255. SelectListView.prototype.selectNextItemView = function() {
  256. var view;
  257. view = this.getSelectedItemView().next();
  258. if (!view.length) {
  259. view = this.list.find('li:first');
  260. }
  261. return this.selectItemView(view);
  262. };
  263. SelectListView.prototype.selectItemView = function(view) {
  264. if (!view.length) {
  265. return;
  266. }
  267. this.list.find('.selected').removeClass('selected');
  268. view.addClass('selected');
  269. return this.scrollToItemView(view);
  270. };
  271. SelectListView.prototype.scrollToItemView = function(view) {
  272. var desiredBottom, desiredTop, scrollTop;
  273. scrollTop = this.list.scrollTop();
  274. desiredTop = view.position().top + scrollTop;
  275. desiredBottom = desiredTop + view.outerHeight();
  276. if (desiredTop < scrollTop) {
  277. return this.list.scrollTop(desiredTop);
  278. } else if (desiredBottom > this.list.scrollBottom()) {
  279. return this.list.scrollBottom(desiredBottom);
  280. }
  281. };
  282. SelectListView.prototype.restoreFocus = function() {
  283. var _ref1;
  284. return (_ref1 = this.previouslyFocusedElement) != null ? _ref1.focus() : void 0;
  285. };
  286. SelectListView.prototype.getSelectedItemView = function() {
  287. return this.list.find('li.selected');
  288. };
  289. SelectListView.prototype.confirmSelection = function() {
  290. var item;
  291. item = this.getSelectedItem();
  292. if (item != null) {
  293. return this.confirmed(item);
  294. } else {
  295. return this.cancel();
  296. }
  297. };
  298. SelectListView.prototype.schedulePopulateList = function() {
  299. var populateCallback;
  300. clearTimeout(this.scheduleTimeout);
  301. populateCallback = (function(_this) {
  302. return function() {
  303. if (_this.isOnDom()) {
  304. return _this.populateList();
  305. }
  306. };
  307. })(this);
  308. return this.scheduleTimeout = setTimeout(populateCallback, this.inputThrottle);
  309. };
  310. return SelectListView;
  311. })(View);
  312. }).call(this);