sidebar.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. app.views.Sidebar = class Sidebar extends app.View {
  2. static el = "._sidebar";
  3. static events = {
  4. focus: "onFocus",
  5. select: "onSelect",
  6. click: "onClick",
  7. };
  8. static routes = { after: "afterRoute" };
  9. static shortcuts = {
  10. altR: "onAltR",
  11. escape: "onEscape",
  12. };
  13. init() {
  14. if (!app.isMobile()) {
  15. this.addSubview((this.hover = new app.views.SidebarHover(this.el)));
  16. }
  17. this.addSubview((this.search = new app.views.Search()));
  18. this.search
  19. .on("searching", () => this.onSearching())
  20. .on("clear", () => this.onSearchClear())
  21. .scope.on("change", (newDoc, previousDoc) =>
  22. this.onScopeChange((newDoc, previousDoc)),
  23. );
  24. this.results = new app.views.Results(this, this.search);
  25. this.docList = new app.views.DocList();
  26. app.on("ready", () => this.onReady());
  27. $.on(document.documentElement, "mouseleave", () => this.hide());
  28. $.on(document.documentElement, "mouseenter", () =>
  29. this.resetDisplay({ forceNoHover: false }),
  30. );
  31. }
  32. hide() {
  33. this.removeClass("show");
  34. }
  35. display() {
  36. this.addClass("show");
  37. }
  38. resetDisplay(options) {
  39. if (options == null) {
  40. options = {};
  41. }
  42. if (!this.hasClass("show")) {
  43. return;
  44. }
  45. this.removeClass("show");
  46. if (options.forceNoHover !== false && !this.hasClass("no-hover")) {
  47. this.addClass("no-hover");
  48. this.resetHoverOnMouseMove = this.resetHoverOnMouseMove.bind(this);
  49. $.on(window, "mousemove", this.resetHoverOnMouseMove);
  50. }
  51. }
  52. resetHoverOnMouseMove() {
  53. $.off(window, "mousemove", this.resetHoverOnMouseMove);
  54. return requestAnimationFrame(() => this.resetHover());
  55. }
  56. resetHover() {
  57. return this.removeClass("no-hover");
  58. }
  59. showView(view) {
  60. if (this.view !== view) {
  61. if (this.hover != null) {
  62. this.hover.hide();
  63. }
  64. this.saveScrollPosition();
  65. if (this.view != null) {
  66. this.view.deactivate();
  67. }
  68. this.view = view;
  69. this.render();
  70. this.view.activate();
  71. this.restoreScrollPosition();
  72. }
  73. }
  74. render() {
  75. this.html(this.view);
  76. }
  77. showDocList() {
  78. this.showView(this.docList);
  79. }
  80. showResults() {
  81. this.display();
  82. this.showView(this.results);
  83. }
  84. reset() {
  85. this.display();
  86. this.showDocList();
  87. this.docList.reset();
  88. this.search.reset();
  89. }
  90. onReady() {
  91. this.view = this.docList;
  92. this.render();
  93. this.view.activate();
  94. }
  95. onScopeChange(newDoc, previousDoc) {
  96. if (previousDoc) {
  97. this.docList.closeDoc(previousDoc);
  98. }
  99. if (newDoc) {
  100. this.docList.reveal(newDoc.toEntry());
  101. } else {
  102. this.scrollToTop();
  103. }
  104. }
  105. saveScrollPosition() {
  106. if (this.view === this.docList) {
  107. this.scrollTop = this.el.scrollTop;
  108. }
  109. }
  110. restoreScrollPosition() {
  111. if (this.view === this.docList && this.scrollTop) {
  112. this.el.scrollTop = this.scrollTop;
  113. this.scrollTop = null;
  114. } else {
  115. this.scrollToTop();
  116. }
  117. }
  118. scrollToTop() {
  119. this.el.scrollTop = 0;
  120. }
  121. onSearching() {
  122. this.showResults();
  123. }
  124. onSearchClear() {
  125. this.resetDisplay();
  126. this.showDocList();
  127. }
  128. onFocus(event) {
  129. this.display();
  130. if (event.target !== this.el) {
  131. $.scrollTo(event.target, this.el, "continuous", { bottomGap: 2 });
  132. }
  133. }
  134. onSelect() {
  135. this.resetDisplay();
  136. }
  137. onClick(event) {
  138. if (event.which !== 1) {
  139. return;
  140. }
  141. if ($.eventTarget(event).hasAttribute?.("data-reset-list")) {
  142. $.stopEvent(event);
  143. this.onAltR();
  144. }
  145. }
  146. onAltR() {
  147. this.reset();
  148. this.docList.reset({ revealCurrent: true });
  149. this.display();
  150. }
  151. onEscape() {
  152. let doc;
  153. this.reset();
  154. this.resetDisplay();
  155. if ((doc = this.search.getScopeDoc())) {
  156. this.docList.reveal(doc.toEntry());
  157. } else {
  158. this.scrollToTop();
  159. }
  160. }
  161. onDocEnabled() {
  162. this.docList.onEnabled();
  163. this.reset();
  164. }
  165. afterRoute(name, context) {
  166. if (
  167. (app.shortcuts.eventInProgress != null
  168. ? app.shortcuts.eventInProgress.name
  169. : undefined) === "escape"
  170. ) {
  171. return;
  172. }
  173. if (!context.init && app.router.isIndex()) {
  174. this.reset();
  175. }
  176. this.resetDisplay();
  177. }
  178. };