|
4 | 4 | (function() { |
5 | 5 | 'use strict'; |
6 | 6 |
|
| 7 | + const { HtmlUtils } = window.edx; |
| 8 | + |
7 | 9 | this.Sequence = (function() { |
8 | 10 | function Sequence(element, runtime) { |
9 | 11 | var self = this; |
|
26 | 28 | this.goto = function(event) { |
27 | 29 | return Sequence.prototype.goto.apply(self, [event]); |
28 | 30 | }; |
| 31 | + this.toggleDropdown = function(event) { |
| 32 | + return Sequence.prototype.toggleDropdown.apply(self, [event]); |
| 33 | + }; |
29 | 34 | this.toggleArrows = function() { |
30 | 35 | return Sequence.prototype.toggleArrows.apply(self); |
31 | 36 | }; |
|
38 | 43 | this.displayTabTooltip = function(event) { |
39 | 44 | return Sequence.prototype.displayTabTooltip.apply(self, [event]); |
40 | 45 | }; |
| 46 | + this.renderDropdown = function() { |
| 47 | + return Sequence.prototype.renderDropdown.apply(self); |
| 48 | + } |
| 49 | + this.handleClickOutsideDropdown = function(event) { |
| 50 | + return Sequence.prototype.handleClickOutsideDropdown.apply(self, [event]); |
| 51 | + } |
41 | 52 | this.arrowKeys = { |
42 | 53 | LEFT: 37, |
43 | 54 | UP: 38, |
|
62 | 73 | this.showCompletion = this.el.data('show-completion'); |
63 | 74 | this.keydownHandler($(element).find('#sequence-list .tab')); |
64 | 75 | this.base_page_title = ($('title').data('base-title') || '').trim(); |
| 76 | + this.dropdownButtonTpl = _.template($('#dropdown-button-tpl').text())({}); |
| 77 | + this.renderDropdown(); |
65 | 78 | this.bind(); |
66 | 79 | this.render(parseInt(this.el.data('position'), 10)); |
67 | 80 | } |
68 | 81 |
|
| 82 | + Sequence.prototype.renderDropdown = function() { |
| 83 | + // Renders the dropdown when there isn't enough space for all units in the bar |
| 84 | + // Hide the dropdown by default and only show if needed. |
| 85 | + this.$('#sequence-list > #dropdown-container').hide(); |
| 86 | + this.$(`#sequence-list > li.sequence-list-item`).show(); |
| 87 | + // Calculate the number of tabs that can fit comfortably and if the |
| 88 | + // number of units is greater we show the dropdown. |
| 89 | + const tabListWidth = this.$('#sequence-list').width(); |
| 90 | + const singleTabWidth = this.$('#sequence-list > li:first').width(); |
| 91 | + const tabCount = this.$('#sequence-list > li.sequence-list-item').length; |
| 92 | + const overFlowCount = Math.floor(tabListWidth / singleTabWidth); |
| 93 | + // Reduce 1 to offsets index and another one to accommodate the button |
| 94 | + const overFlowIdx = overFlowCount - 2; |
| 95 | + const showDropdown = overFlowCount < tabCount; |
| 96 | + if (!showDropdown) { |
| 97 | + return; |
| 98 | + } |
| 99 | + // If the dropdown button doesn't exist add it, otherwise move the |
| 100 | + // existing button to the correct place. |
| 101 | + if (this.$('#sequence-list > #dropdown-container').length === 0) { |
| 102 | + // xss-lint: disable=javascript-jquery-insertion |
| 103 | + this.$('#sequence-list > li.sequence-list-item').eq(overFlowIdx).after(this.dropdownButtonTpl); |
| 104 | + } else { |
| 105 | + this.$('#sequence-list > li.sequence-list-item').eq(overFlowIdx) |
| 106 | + // xss-lint: disable=javascript-jquery-insertion |
| 107 | + .after(this.$('#sequence-list > #dropdown-container')); |
| 108 | + } |
| 109 | + // Show the dropdown UX and hide all the overflowing unit buttons. |
| 110 | + this.$('#sequence-list > #dropdown-container').show(); |
| 111 | + this.$(`#sequence-list > li.sequence-list-item:lt(${overFlowIdx + 1})`).show(); |
| 112 | + this.$(`#sequence-list > li.sequence-list-item:gt(${overFlowIdx})`).hide(); |
| 113 | + const dropdownList = this.$('#dropdown-sequence-list > ol'); |
| 114 | + // The dropdown buttons are modified copies of the unit nav buttons. |
| 115 | + dropdownList.empty(); |
| 116 | + this.$(`#sequence-list > li.sequence-list-item:gt(${overFlowIdx})`).each((idx, el) => { |
| 117 | + const cloneEl = $(el).clone(); |
| 118 | + const navButton = cloneEl.find("button"); |
| 119 | + const unitTitle = navButton.data('page-title'); |
| 120 | + navButton.click(self.goto); |
| 121 | + navButton.find(".sequence-tooltip").remove(); |
| 122 | + navButton.find("span.icon").after( |
| 123 | + HtmlUtils.joinHtml(HtmlUtils.HTML('<span class="unit-title">'), unitTitle, HtmlUtils.HTML('</span>')).toString() |
| 124 | + ); |
| 125 | + //xss-lint: disable=javascript-jquery-insert-into-target |
| 126 | + cloneEl.show().appendTo(dropdownList); |
| 127 | + }); |
| 128 | + } |
| 129 | + |
69 | 130 | Sequence.prototype.$ = function(selector) { |
70 | 131 | return $(selector, this.el); |
71 | 132 | }; |
72 | 133 |
|
73 | 134 | Sequence.prototype.bind = function() { |
74 | 135 | this.$('#sequence-list .nav-item').click(this.goto); |
| 136 | + $(document).click(this.handleClickOutsideDropdown); |
| 137 | + this.$('#dropdown-sequence-list .dropdown-item').click(this.goto); |
| 138 | + this.$('#dropdown-sequence-list-button').click(this.toggleDropdown); |
75 | 139 | this.$('#sequence-list .nav-item').keypress(this.keyDownHandler); |
76 | 140 | this.el.on('bookmark:add', this.addBookmarkIconToActiveNavItem); |
77 | 141 | this.el.on('bookmark:remove', this.removeBookmarkIconFromActiveNavItem); |
78 | 142 | this.$('#sequence-list .nav-item').on('focus mouseenter', this.displayTabTooltip); |
79 | 143 | this.$('#sequence-list .nav-item').on('blur mouseleave', this.hideTabTooltip); |
| 144 | + $(window).on('resize', _.debounce(this.renderDropdown.bind(this), 200)); |
80 | 145 | }; |
81 | 146 |
|
| 147 | + Sequence.prototype.handleClickOutsideDropdown = function(event) { |
| 148 | + if(!this.$('#dropdown-container')?.[0]?.contains(event.target)) { |
| 149 | + this.$('#dropdown-sequence-list').hide(); |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + Sequence.prototype.toggleDropdown = function() { |
| 154 | + $('#dropdown-sequence-list').toggle(); |
| 155 | + } |
| 156 | + |
82 | 157 | Sequence.prototype.previousNav = function(focused, index) { |
83 | 158 | var $navItemList, |
84 | 159 | $sequenceList = $(focused).parent().parent(); |
|
289 | 364 | Sequence.prototype.goto = function(event) { |
290 | 365 | var alertTemplate, alertText, isBottomNav, newPosition, widgetPlacement; |
291 | 366 | event.preventDefault(); |
| 367 | + this.$('#dropdown-sequence-list').hide(); |
292 | 368 |
|
293 | 369 | // Links from courseware <a class='seqnav' href='n'>...</a>, was .target_tab |
294 | 370 | if ($(event.currentTarget).hasClass('seqnav')) { |
|
0 commit comments