sidebar_hover.js 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. app.views.SidebarHover = class SidebarHover extends app.View {
  2. static itemClass = "_list-hover";
  3. static events = {
  4. focus: "onFocus",
  5. blur: "onBlur",
  6. mouseover: "onMouseover",
  7. mouseout: "onMouseout",
  8. scroll: "onScroll",
  9. click: "onClick",
  10. };
  11. static routes = { after: "onRoute" };
  12. show(el) {
  13. if (el !== this.cursor) {
  14. this.hide();
  15. if (this.isTarget(el) && this.isTruncated(el.lastElementChild || el)) {
  16. this.cursor = el;
  17. this.clone = this.makeClone(this.cursor);
  18. $.append(document.body, this.clone);
  19. if (this.offsetTop == null) {
  20. this.offsetTop = this.el.offsetTop;
  21. }
  22. this.position();
  23. }
  24. }
  25. }
  26. hide() {
  27. if (this.cursor) {
  28. $.remove(this.clone);
  29. this.cursor = this.clone = null;
  30. }
  31. }
  32. position() {
  33. if (this.cursor) {
  34. const rect = $.rect(this.cursor);
  35. if (rect.top >= this.offsetTop) {
  36. this.clone.style.top = rect.top + "px";
  37. this.clone.style.left = rect.left + "px";
  38. } else {
  39. this.hide();
  40. }
  41. }
  42. }
  43. makeClone(el) {
  44. const clone = el.cloneNode(true);
  45. clone.classList.add("clone");
  46. return clone;
  47. }
  48. isTarget(el) {
  49. return el.classList?.contains(this.constructor.itemClass);
  50. }
  51. isSelected(el) {
  52. return el.classList.contains("active");
  53. }
  54. isTruncated(el) {
  55. return el.scrollWidth > el.offsetWidth;
  56. }
  57. onFocus(event) {
  58. this.focusTime = Date.now();
  59. this.show(event.target);
  60. }
  61. onBlur() {
  62. this.hide();
  63. }
  64. onMouseover(event) {
  65. if (
  66. this.isTarget(event.target) &&
  67. !this.isSelected(event.target) &&
  68. this.mouseActivated()
  69. ) {
  70. this.show(event.target);
  71. }
  72. }
  73. onMouseout(event) {
  74. if (this.isTarget(event.target) && this.mouseActivated()) {
  75. this.hide();
  76. }
  77. }
  78. mouseActivated() {
  79. // Skip mouse events caused by focus events scrolling the sidebar.
  80. return !this.focusTime || Date.now() - this.focusTime > 500;
  81. }
  82. onScroll() {
  83. this.position();
  84. }
  85. onClick(event) {
  86. if (event.target === this.clone) {
  87. $.click(this.cursor);
  88. }
  89. }
  90. onRoute() {
  91. this.hide();
  92. }
  93. };