Fossil SCM
Forum post collapse/expand: 1) Is now full width to balance visibility and mobile accessibility. 2) Use arrows (instead of words) to convey intent. 3) When collapsing, scroll the collapser widget to the top of the viewport so that the next post is in view (avoids manual scrolling back up). 4) double-click on a large post toggles expand/collapse (avoids scrolling when expanded).
Commit
a30a6db480ece85773abf8923db91a5b48e3c5fefe7b479b8ddb7d523c73d4d3
Parent
d4c45b50a956980…
2 files changed
+27
-3
+22
-33
+27
-3
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -773,13 +773,37 @@ | ||
| 773 | 773 | div.forumHier > div > form, |
| 774 | 774 | div.forumTime > div > form, |
| 775 | 775 | div.forumHierRoot > div > form { |
| 776 | 776 | margin: 0.5em 0; |
| 777 | 777 | } |
| 778 | -button.forum-post-collapser { | |
| 779 | - align-self: flex-start; | |
| 780 | - margin-top: 0.1em; | |
| 778 | +.forum-post-collapser { | |
| 779 | + font-size: 0.8em; | |
| 780 | + margin-top: 0.2em; | |
| 781 | + padding: 0; | |
| 782 | + height: 1.75em; | |
| 783 | + line-height: 1.75em; | |
| 784 | + /* ^^^ Those sizes are finely tuned for the current selection of | |
| 785 | + arrow characters. If those change, these should, too. Remember that | |
| 786 | + FF/Chrome simply do not agree on alignment with most values :/. */ | |
| 787 | + border-width: 1px; | |
| 788 | + border-style: solid; | |
| 789 | + border-radius: 0.25em; | |
| 790 | + opacity: 0.8; | |
| 791 | + cursor: pointer; | |
| 792 | + display: flex; | |
| 793 | + flex-direction: row; | |
| 794 | + justify-content: space-between; | |
| 795 | +} | |
| 796 | +.forum-post-collapser > span { | |
| 797 | + margin: 0 1em 0 1em; | |
| 798 | + vertical-align: middle; | |
| 799 | +} | |
| 800 | +.forum-post-collapser.expanded > span::before { | |
| 801 | + content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of ⮝*/; | |
| 802 | +} | |
| 803 | +.forum-post-collapser:not(.expanded) > span::before { | |
| 804 | + content: "⇣⇣⇣"; | |
| 781 | 805 | } |
| 782 | 806 | div.forumPostBody{ |
| 783 | 807 | max-height: 50em; |
| 784 | 808 | overflow: auto; |
| 785 | 809 | } |
| 786 | 810 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -773,13 +773,37 @@ | |
| 773 | div.forumHier > div > form, |
| 774 | div.forumTime > div > form, |
| 775 | div.forumHierRoot > div > form { |
| 776 | margin: 0.5em 0; |
| 777 | } |
| 778 | button.forum-post-collapser { |
| 779 | align-self: flex-start; |
| 780 | margin-top: 0.1em; |
| 781 | } |
| 782 | div.forumPostBody{ |
| 783 | max-height: 50em; |
| 784 | overflow: auto; |
| 785 | } |
| 786 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -773,13 +773,37 @@ | |
| 773 | div.forumHier > div > form, |
| 774 | div.forumTime > div > form, |
| 775 | div.forumHierRoot > div > form { |
| 776 | margin: 0.5em 0; |
| 777 | } |
| 778 | .forum-post-collapser { |
| 779 | font-size: 0.8em; |
| 780 | margin-top: 0.2em; |
| 781 | padding: 0; |
| 782 | height: 1.75em; |
| 783 | line-height: 1.75em; |
| 784 | /* ^^^ Those sizes are finely tuned for the current selection of |
| 785 | arrow characters. If those change, these should, too. Remember that |
| 786 | FF/Chrome simply do not agree on alignment with most values :/. */ |
| 787 | border-width: 1px; |
| 788 | border-style: solid; |
| 789 | border-radius: 0.25em; |
| 790 | opacity: 0.8; |
| 791 | cursor: pointer; |
| 792 | display: flex; |
| 793 | flex-direction: row; |
| 794 | justify-content: space-between; |
| 795 | } |
| 796 | .forum-post-collapser > span { |
| 797 | margin: 0 1em 0 1em; |
| 798 | vertical-align: middle; |
| 799 | } |
| 800 | .forum-post-collapser.expanded > span::before { |
| 801 | content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of ⮝*/; |
| 802 | } |
| 803 | .forum-post-collapser:not(.expanded) > span::before { |
| 804 | content: "⇣⇣⇣"; |
| 805 | } |
| 806 | div.forumPostBody{ |
| 807 | max-height: 50em; |
| 808 | overflow: auto; |
| 809 | } |
| 810 |
+22
-33
| --- src/fossil.page.forumpost.js | ||
| +++ src/fossil.page.forumpost.js | ||
| @@ -3,60 +3,49 @@ | ||
| 3 | 3 | /* JS code for /forumpage and friends. Requires fossil.dom. */ |
| 4 | 4 | const P = fossil.page, D = fossil.dom; |
| 5 | 5 | |
| 6 | 6 | F.onPageLoad(function(){ |
| 7 | 7 | const scrollbarIsVisible = (e)=>e.scrollHeight > e.clientHeight; |
| 8 | - const getButtonHandler = function(btn, contentElem){ | |
| 8 | + /* Returns an event handler which implements the post expand/collapse toggle | |
| 9 | + on contentElem when the given widget is activated. */ | |
| 10 | + const getWidgetHandler = function(widget, contentElem){ | |
| 9 | 11 | return function(ev){ |
| 10 | 12 | if(ev) ev.preventDefault(); |
| 11 | - const isExpanded = D.hasClass(contentElem,'expanded'); | |
| 12 | - btn.innerText = isExpanded ? 'Expand...' : 'Collapse'; | |
| 13 | + const wasExpanded = widget.classList.contains('expanded'); | |
| 14 | + widget.classList.toggle('expanded'); | |
| 13 | 15 | contentElem.classList.toggle('expanded'); |
| 16 | + if(wasExpanded) widget.scrollIntoView(); | |
| 14 | 17 | return false; |
| 15 | 18 | }; |
| 16 | 19 | }; |
| 17 | - /** Install an event handler on element e which calls the given | |
| 18 | - callback if the user presses the element for a brief period | |
| 19 | - (time is defined a few lines down from here). */ | |
| 20 | - const addLongpressHandler = function(e, callback){ | |
| 21 | - const longPressTime = 650 /*ms*/; | |
| 22 | - var timer; | |
| 23 | - const clearTimer = function(){ | |
| 24 | - if(timer){ | |
| 25 | - clearTimeout(timer); | |
| 26 | - timer = undefined; | |
| 27 | - } | |
| 28 | - }; | |
| 29 | - e.addEventListener('mousedown', function(ev){ | |
| 30 | - timer = setTimeout(function(){ | |
| 31 | - clearTimer(); | |
| 32 | - callback(); | |
| 33 | - }, longPressTime); | |
| 34 | - }, false); | |
| 35 | - e.addEventListener('mouseup', clearTimer, false); | |
| 36 | - e.addEventListener('mouseout', clearTimer, false); | |
| 37 | - }; | |
| 38 | 20 | /* Adds an Expand/Collapse toggle to all div.forumPostBody |
| 39 | 21 | elements which are deemed "too large" (those for which |
| 40 | 22 | scrolling is currently activated because they are taller than |
| 41 | 23 | their max-height). */ |
| 42 | 24 | document.querySelectorAll( |
| 43 | 25 | 'div.forumHier, div.forumTime, div.forumHierRoot' |
| 44 | 26 | ).forEach(function(forumPostWrapper){ |
| 45 | 27 | const content = forumPostWrapper.querySelector('div.forumPostBody'); |
| 46 | 28 | if(!content || !scrollbarIsVisible(content)) return; |
| 47 | - const button = D.button('Expand...'), | |
| 48 | - btnEventHandler = getButtonHandler(button, content); | |
| 49 | - button.classList.add('forum-post-collapser'); | |
| 50 | - button.addEventListener('click', btnEventHandler, false); | |
| 29 | + const widget = D.div(), | |
| 30 | + widgetEventHandler = getWidgetHandler(widget, content); | |
| 31 | + widget.classList.add('forum-post-collapser'); | |
| 32 | + widget.addEventListener('click', widgetEventHandler, false); | |
| 33 | + /** Append 3 children, which CSS will evenly space across the | |
| 34 | + widget. This improves visibility over having the label | |
| 35 | + in only the left, right, or center. */ | |
| 36 | + var i = 0; | |
| 37 | + for( ; i < 3; ++i ) D.append(widget, D.span()); | |
| 51 | 38 | if(content.nextSibling){ |
| 52 | - forumPostWrapper.insertBefore(button, content.nextSibling); | |
| 39 | + forumPostWrapper.insertBefore(widget, content.nextSibling); | |
| 53 | 40 | }else{ |
| 54 | - forumPostWrapper.appendChild(button); | |
| 41 | + forumPostWrapper.appendChild(widget); | |
| 55 | 42 | } |
| 56 | - // uncomment to enable long-press expand/collapse toggle: | |
| 57 | - // addLongpressHandler(content, btnEventHandler); | |
| 58 | - // It may interfere with default actions on mobile platforms, though. | |
| 43 | + /** A double-click toggle will select "the current word" on the | |
| 44 | + post, which is minorly annoying but otherwise harmless. Such | |
| 45 | + a toggle has proven convenient on "excessive" posts, | |
| 46 | + though. */ | |
| 47 | + content.addEventListener('dblclick', widgetEventHandler); | |
| 59 | 48 | }); |
| 60 | 49 | })/*onload callback*/; |
| 61 | 50 | |
| 62 | 51 | })(window.fossil); |
| 63 | 52 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -3,60 +3,49 @@ | |
| 3 | /* JS code for /forumpage and friends. Requires fossil.dom. */ |
| 4 | const P = fossil.page, D = fossil.dom; |
| 5 | |
| 6 | F.onPageLoad(function(){ |
| 7 | const scrollbarIsVisible = (e)=>e.scrollHeight > e.clientHeight; |
| 8 | const getButtonHandler = function(btn, contentElem){ |
| 9 | return function(ev){ |
| 10 | if(ev) ev.preventDefault(); |
| 11 | const isExpanded = D.hasClass(contentElem,'expanded'); |
| 12 | btn.innerText = isExpanded ? 'Expand...' : 'Collapse'; |
| 13 | contentElem.classList.toggle('expanded'); |
| 14 | return false; |
| 15 | }; |
| 16 | }; |
| 17 | /** Install an event handler on element e which calls the given |
| 18 | callback if the user presses the element for a brief period |
| 19 | (time is defined a few lines down from here). */ |
| 20 | const addLongpressHandler = function(e, callback){ |
| 21 | const longPressTime = 650 /*ms*/; |
| 22 | var timer; |
| 23 | const clearTimer = function(){ |
| 24 | if(timer){ |
| 25 | clearTimeout(timer); |
| 26 | timer = undefined; |
| 27 | } |
| 28 | }; |
| 29 | e.addEventListener('mousedown', function(ev){ |
| 30 | timer = setTimeout(function(){ |
| 31 | clearTimer(); |
| 32 | callback(); |
| 33 | }, longPressTime); |
| 34 | }, false); |
| 35 | e.addEventListener('mouseup', clearTimer, false); |
| 36 | e.addEventListener('mouseout', clearTimer, false); |
| 37 | }; |
| 38 | /* Adds an Expand/Collapse toggle to all div.forumPostBody |
| 39 | elements which are deemed "too large" (those for which |
| 40 | scrolling is currently activated because they are taller than |
| 41 | their max-height). */ |
| 42 | document.querySelectorAll( |
| 43 | 'div.forumHier, div.forumTime, div.forumHierRoot' |
| 44 | ).forEach(function(forumPostWrapper){ |
| 45 | const content = forumPostWrapper.querySelector('div.forumPostBody'); |
| 46 | if(!content || !scrollbarIsVisible(content)) return; |
| 47 | const button = D.button('Expand...'), |
| 48 | btnEventHandler = getButtonHandler(button, content); |
| 49 | button.classList.add('forum-post-collapser'); |
| 50 | button.addEventListener('click', btnEventHandler, false); |
| 51 | if(content.nextSibling){ |
| 52 | forumPostWrapper.insertBefore(button, content.nextSibling); |
| 53 | }else{ |
| 54 | forumPostWrapper.appendChild(button); |
| 55 | } |
| 56 | // uncomment to enable long-press expand/collapse toggle: |
| 57 | // addLongpressHandler(content, btnEventHandler); |
| 58 | // It may interfere with default actions on mobile platforms, though. |
| 59 | }); |
| 60 | })/*onload callback*/; |
| 61 | |
| 62 | })(window.fossil); |
| 63 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -3,60 +3,49 @@ | |
| 3 | /* JS code for /forumpage and friends. Requires fossil.dom. */ |
| 4 | const P = fossil.page, D = fossil.dom; |
| 5 | |
| 6 | F.onPageLoad(function(){ |
| 7 | const scrollbarIsVisible = (e)=>e.scrollHeight > e.clientHeight; |
| 8 | /* Returns an event handler which implements the post expand/collapse toggle |
| 9 | on contentElem when the given widget is activated. */ |
| 10 | const getWidgetHandler = function(widget, contentElem){ |
| 11 | return function(ev){ |
| 12 | if(ev) ev.preventDefault(); |
| 13 | const wasExpanded = widget.classList.contains('expanded'); |
| 14 | widget.classList.toggle('expanded'); |
| 15 | contentElem.classList.toggle('expanded'); |
| 16 | if(wasExpanded) widget.scrollIntoView(); |
| 17 | return false; |
| 18 | }; |
| 19 | }; |
| 20 | /* Adds an Expand/Collapse toggle to all div.forumPostBody |
| 21 | elements which are deemed "too large" (those for which |
| 22 | scrolling is currently activated because they are taller than |
| 23 | their max-height). */ |
| 24 | document.querySelectorAll( |
| 25 | 'div.forumHier, div.forumTime, div.forumHierRoot' |
| 26 | ).forEach(function(forumPostWrapper){ |
| 27 | const content = forumPostWrapper.querySelector('div.forumPostBody'); |
| 28 | if(!content || !scrollbarIsVisible(content)) return; |
| 29 | const widget = D.div(), |
| 30 | widgetEventHandler = getWidgetHandler(widget, content); |
| 31 | widget.classList.add('forum-post-collapser'); |
| 32 | widget.addEventListener('click', widgetEventHandler, false); |
| 33 | /** Append 3 children, which CSS will evenly space across the |
| 34 | widget. This improves visibility over having the label |
| 35 | in only the left, right, or center. */ |
| 36 | var i = 0; |
| 37 | for( ; i < 3; ++i ) D.append(widget, D.span()); |
| 38 | if(content.nextSibling){ |
| 39 | forumPostWrapper.insertBefore(widget, content.nextSibling); |
| 40 | }else{ |
| 41 | forumPostWrapper.appendChild(widget); |
| 42 | } |
| 43 | /** A double-click toggle will select "the current word" on the |
| 44 | post, which is minorly annoying but otherwise harmless. Such |
| 45 | a toggle has proven convenient on "excessive" posts, |
| 46 | though. */ |
| 47 | content.addEventListener('dblclick', widgetEventHandler); |
| 48 | }); |
| 49 | })/*onload callback*/; |
| 50 | |
| 51 | })(window.fossil); |
| 52 |