-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Use dropdown for units when there are more than would fit in the UI #321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <p> | ||
| <h2><%- title %></h2> | ||
| <table class="stat_table"><tr> | ||
| <%_.forEach(header, function (h) {%> | ||
| <th><%- h %></th> | ||
| <%})%> | ||
| </tr> | ||
| <%_.forEach(data, function (row) {%> | ||
| <tr> | ||
| <%_.forEach(row, function (value) {%> | ||
| <td><%- value %></td> | ||
| <%})%> | ||
| </tr> | ||
| <%})%> | ||
| </table> | ||
| </p> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ | |
| (function() { | ||
| 'use strict'; | ||
|
|
||
| const { HtmlUtils } = window.edx; | ||
|
|
||
| this.Sequence = (function() { | ||
| function Sequence(element, runtime) { | ||
| var self = this; | ||
|
|
@@ -26,6 +28,9 @@ | |
| this.goto = function(event) { | ||
| return Sequence.prototype.goto.apply(self, [event]); | ||
| }; | ||
| this.toggleDropdown = function(event) { | ||
| return Sequence.prototype.toggleDropdown.apply(self, [event]); | ||
| }; | ||
| this.toggleArrows = function() { | ||
| return Sequence.prototype.toggleArrows.apply(self); | ||
| }; | ||
|
|
@@ -38,6 +43,12 @@ | |
| this.displayTabTooltip = function(event) { | ||
| return Sequence.prototype.displayTabTooltip.apply(self, [event]); | ||
| }; | ||
| this.renderDropdown = function() { | ||
| return Sequence.prototype.renderDropdown.apply(self); | ||
| } | ||
| this.handleClickOutsideDropdown = function(event) { | ||
| return Sequence.prototype.handleClickOutsideDropdown.apply(self, [event]); | ||
| } | ||
| this.arrowKeys = { | ||
| LEFT: 37, | ||
| UP: 38, | ||
|
|
@@ -62,23 +73,87 @@ | |
| this.showCompletion = this.el.data('show-completion'); | ||
| this.keydownHandler($(element).find('#sequence-list .tab')); | ||
| this.base_page_title = ($('title').data('base-title') || '').trim(); | ||
| this.dropdownButtonTpl = _.template($('#dropdown-button-tpl').text())({}); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The template To handle multiple instances, consider:
This is a critical issue if multiple sequence blocks can appear on one page. |
||
| this.renderDropdown(); | ||
| this.bind(); | ||
| this.render(parseInt(this.el.data('position'), 10)); | ||
| } | ||
|
|
||
| Sequence.prototype.renderDropdown = function() { | ||
| // Renders the dropdown when there isn't enough space for all units in the bar | ||
| // Hide the dropdown by default and only show if needed. | ||
| this.$('#sequence-list > #dropdown-container').hide(); | ||
| this.$(`#sequence-list > li.sequence-list-item`).show(); | ||
| // Calculate the number of tabs that can fit comfortably and if the | ||
| // number of units is greater we show the dropdown. | ||
| const tabListWidth = this.$('#sequence-list').width(); | ||
| const singleTabWidth = this.$('#sequence-list > li:first').width(); | ||
| const tabCount = this.$('#sequence-list > li.sequence-list-item').length; | ||
| const overFlowCount = Math.floor(tabListWidth / singleTabWidth); | ||
| // Reduce 1 to offsets index and another one to accommodate the button | ||
| const overFlowIdx = overFlowCount - 2; | ||
| const showDropdown = overFlowCount < tabCount; | ||
| if (!showDropdown) { | ||
| return; | ||
| } | ||
| // If the dropdown button doesn't exist add it, otherwise move the | ||
| // existing button to the correct place. | ||
| if (this.$('#sequence-list > #dropdown-container').length === 0) { | ||
| // xss-lint: disable=javascript-jquery-insertion | ||
| this.$('#sequence-list > li.sequence-list-item').eq(overFlowIdx).after(this.dropdownButtonTpl); | ||
| } else { | ||
| this.$('#sequence-list > li.sequence-list-item').eq(overFlowIdx) | ||
| // xss-lint: disable=javascript-jquery-insertion | ||
| .after(this.$('#sequence-list > #dropdown-container')); | ||
| } | ||
| // Show the dropdown UX and hide all the overflowing unit buttons. | ||
| this.$('#sequence-list > #dropdown-container').show(); | ||
| this.$(`#sequence-list > li.sequence-list-item:lt(${overFlowIdx + 1})`).show(); | ||
| this.$(`#sequence-list > li.sequence-list-item:gt(${overFlowIdx})`).hide(); | ||
| const dropdownList = this.$('#dropdown-sequence-list > ol'); | ||
| // The dropdown buttons are modified copies of the unit nav buttons. | ||
| dropdownList.empty(); | ||
| this.$(`#sequence-list > li.sequence-list-item:gt(${overFlowIdx})`).each((idx, el) => { | ||
| const cloneEl = $(el).clone(); | ||
| const navButton = cloneEl.find("button"); | ||
| const unitTitle = navButton.data('page-title'); | ||
| navButton.click(self.goto); | ||
| navButton.find(".sequence-tooltip").remove(); | ||
| navButton.find("span.icon").after( | ||
| HtmlUtils.joinHtml(HtmlUtils.HTML('<span class="unit-title">'), unitTitle, HtmlUtils.HTML('</span>')).toString() | ||
| ); | ||
| //xss-lint: disable=javascript-jquery-insert-into-target | ||
| cloneEl.show().appendTo(dropdownList); | ||
| }); | ||
| } | ||
|
|
||
| Sequence.prototype.$ = function(selector) { | ||
| return $(selector, this.el); | ||
| }; | ||
|
|
||
| Sequence.prototype.bind = function() { | ||
| this.$('#sequence-list .nav-item').click(this.goto); | ||
| $(document).click(this.handleClickOutsideDropdown); | ||
| this.$('#dropdown-sequence-list .dropdown-item').click(this.goto); | ||
|
xitij2000 marked this conversation as resolved.
|
||
| this.$('#dropdown-sequence-list-button').click(this.toggleDropdown); | ||
| this.$('#sequence-list .nav-item').keypress(this.keyDownHandler); | ||
| this.el.on('bookmark:add', this.addBookmarkIconToActiveNavItem); | ||
| this.el.on('bookmark:remove', this.removeBookmarkIconFromActiveNavItem); | ||
| this.$('#sequence-list .nav-item').on('focus mouseenter', this.displayTabTooltip); | ||
| this.$('#sequence-list .nav-item').on('blur mouseleave', this.hideTabTooltip); | ||
| $(window).on('resize', _.debounce(this.renderDropdown.bind(this), 200)); | ||
| }; | ||
|
|
||
| Sequence.prototype.handleClickOutsideDropdown = function(event) { | ||
| if(!this.$('#dropdown-container')?.[0]?.contains(event.target)) { | ||
| this.$('#dropdown-sequence-list').hide(); | ||
| } | ||
| } | ||
|
|
||
| Sequence.prototype.toggleDropdown = function() { | ||
| $('#dropdown-sequence-list').toggle(); | ||
|
xitij2000 marked this conversation as resolved.
|
||
| } | ||
|
|
||
| Sequence.prototype.previousNav = function(focused, index) { | ||
| var $navItemList, | ||
| $sequenceList = $(focused).parent().parent(); | ||
|
|
@@ -289,6 +364,7 @@ | |
| Sequence.prototype.goto = function(event) { | ||
| var alertTemplate, alertText, isBottomNav, newPosition, widgetPlacement; | ||
| event.preventDefault(); | ||
| this.$('#dropdown-sequence-list').hide(); | ||
|
|
||
| // Links from courseware <a class='seqnav' href='n'>...</a>, was .target_tab | ||
| if ($(event.currentTarget).hasClass('seqnav')) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,13 @@ | |
| position: relative; | ||
| height: 100%; | ||
| flex-grow: 1; | ||
| max-width: calc(100% - 80px); | ||
| } | ||
|
|
||
| @media (min-width: 768px) { | ||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav .sequence-list-wrapper { | ||
| max-width: calc(100% - 160px); | ||
| } | ||
| } | ||
|
|
||
| @media (max-width: 575.98px) { | ||
|
|
@@ -91,7 +98,7 @@ | |
| display: flex; | ||
| } | ||
|
|
||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav ol li { | ||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav ol li, .dropdown-toggle { | ||
| box-sizing: border-box; | ||
| min-width: 40px; | ||
| flex-grow: 1; | ||
|
|
@@ -104,7 +111,11 @@ | |
| border-right-style: solid; | ||
| } | ||
|
|
||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav ol li button { | ||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav ol li .dropdown-toggle { | ||
| height: 49px !important; | ||
|
Comment on lines
+114
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav ol li button, .dropdown-toggle { | ||
| width: 100%; | ||
| height: 49px; | ||
| position: relative; | ||
|
|
@@ -119,6 +130,22 @@ | |
| overflow: visible; | ||
| } | ||
|
|
||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav #dropdown-container ol li button { | ||
| display: flex; | ||
| align-items: center; | ||
| padding-left: 0.5rem; | ||
| padding-right: 0.5rem; | ||
| } | ||
|
|
||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav #dropdown-container ol li button .unit-title { | ||
| display: flex; | ||
| flex-grow: 1; | ||
| text-overflow: ellipsis; | ||
| overflow: hidden; | ||
| white-space: nowrap; | ||
| margin: 0 0.5rem; | ||
| } | ||
|
|
||
| .xmodule_display.xmodule_SequenceBlock .sequence-nav ol li button .icon { | ||
| display: inline-block; | ||
| line-height: 100%; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
div#dropdown-sequence-listuses inline styles. For better maintainability and separation of concerns, could these styles be moved to a CSS class inSequenceBlockDisplay.css? This would also make it easier to manage styles consistently.