diff options
author | Eric Eastwood <contact@ericeastwood.com> | 2018-02-07 02:16:57 +0300 |
---|---|---|
committer | Eric Eastwood <contact@ericeastwood.com> | 2018-02-07 02:30:00 +0300 |
commit | a237f294e6dab85779ac20d5aabe891fcdb8ed39 (patch) | |
tree | 532fc73c723e2ba2232141f403088d2df8829c30 /content | |
parent | 5f3270104b1ab8ba9cc1f84a85182ca56007e5e8 (diff) |
Clean up TOC styles and fix bugs
- Clean up unused TOC styles
- Fix bug in sticky TOC where if you scrolled down to the bottom to
make it sticky, then reduce your viewport width to mobile, the TOC
would disappear.
- Fix empty TOC covering header on a path without any TOC
See
https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/182#note_57833460
Diffstat (limited to 'content')
-rw-r--r-- | content/assets/javascripts/classlist-polyfill.js | 240 | ||||
-rw-r--r-- | content/assets/javascripts/docs.js | 14 | ||||
-rw-r--r-- | content/assets/stylesheets/stylesheet.scss | 5 | ||||
-rw-r--r-- | content/assets/stylesheets/toc.scss | 100 |
4 files changed, 286 insertions, 73 deletions
diff --git a/content/assets/javascripts/classlist-polyfill.js b/content/assets/javascripts/classlist-polyfill.js new file mode 100644 index 00000000..09b2e8b0 --- /dev/null +++ b/content/assets/javascripts/classlist-polyfill.js @@ -0,0 +1,240 @@ +/* + * classList.js: Cross-browser full element.classList implementation. + * 1.1.20170427 + * + * By Eli Grey, http://eligrey.com + * License: Dedicated to the public domain. + * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md + */ + +/*global self, document, DOMException */ + +/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ + +if ("document" in window.self) { + +// Full polyfill for browsers with no classList support +// Including IE < Edge missing SVGElement.classList +if (!("classList" in document.createElement("_")) + || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))) { + +(function (view) { + +"use strict"; + +if (!('Element' in view)) return; + +var + classListProp = "classList" + , protoProp = "prototype" + , elemCtrProto = view.Element[protoProp] + , objCtr = Object + , strTrim = String[protoProp].trim || function () { + return this.replace(/^\s+|\s+$/g, ""); + } + , arrIndexOf = Array[protoProp].indexOf || function (item) { + var + i = 0 + , len = this.length + ; + for (; i < len; i++) { + if (i in this && this[i] === item) { + return i; + } + } + return -1; + } + // Vendors: please allow content code to instantiate DOMExceptions + , DOMEx = function (type, message) { + this.name = type; + this.code = DOMException[type]; + this.message = message; + } + , checkTokenAndGetIndex = function (classList, token) { + if (token === "") { + throw new DOMEx( + "SYNTAX_ERR" + , "An invalid or illegal string was specified" + ); + } + if (/\s/.test(token)) { + throw new DOMEx( + "INVALID_CHARACTER_ERR" + , "String contains an invalid character" + ); + } + return arrIndexOf.call(classList, token); + } + , ClassList = function (elem) { + var + trimmedClasses = strTrim.call(elem.getAttribute("class") || "") + , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] + , i = 0 + , len = classes.length + ; + for (; i < len; i++) { + this.push(classes[i]); + } + this._updateClassName = function () { + elem.setAttribute("class", this.toString()); + }; + } + , classListProto = ClassList[protoProp] = [] + , classListGetter = function () { + return new ClassList(this); + } +; +// Most DOMException implementations don't allow calling DOMException's toString() +// on non-DOMExceptions. Error's toString() is sufficient here. +DOMEx[protoProp] = Error[protoProp]; +classListProto.item = function (i) { + return this[i] || null; +}; +classListProto.contains = function (token) { + token += ""; + return checkTokenAndGetIndex(this, token) !== -1; +}; +classListProto.add = function () { + var + tokens = arguments + , i = 0 + , l = tokens.length + , token + , updated = false + ; + do { + token = tokens[i] + ""; + if (checkTokenAndGetIndex(this, token) === -1) { + this.push(token); + updated = true; + } + } + while (++i < l); + + if (updated) { + this._updateClassName(); + } +}; +classListProto.remove = function () { + var + tokens = arguments + , i = 0 + , l = tokens.length + , token + , updated = false + , index + ; + do { + token = tokens[i] + ""; + index = checkTokenAndGetIndex(this, token); + while (index !== -1) { + this.splice(index, 1); + updated = true; + index = checkTokenAndGetIndex(this, token); + } + } + while (++i < l); + + if (updated) { + this._updateClassName(); + } +}; +classListProto.toggle = function (token, force) { + token += ""; + + var + result = this.contains(token) + , method = result ? + force !== true && "remove" + : + force !== false && "add" + ; + + if (method) { + this[method](token); + } + + if (force === true || force === false) { + return force; + } else { + return !result; + } +}; +classListProto.toString = function () { + return this.join(" "); +}; + +if (objCtr.defineProperty) { + var classListPropDesc = { + get: classListGetter + , enumerable: true + , configurable: true + }; + try { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } catch (ex) { // IE 8 doesn't support enumerable:true + // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 + // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected + if (ex.number === undefined || ex.number === -0x7FF5EC54) { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } +} else if (objCtr[protoProp].__defineGetter__) { + elemCtrProto.__defineGetter__(classListProp, classListGetter); +} + +}(window.self)); + +} + +// There is full or partial native classList support, so just check if we need +// to normalize the add/remove and toggle APIs. + +(function () { + "use strict"; + + var testElement = document.createElement("_"); + + testElement.classList.add("c1", "c2"); + + // Polyfill for IE 10/11 and Firefox <26, where classList.add and + // classList.remove exist but support only one argument at a time. + if (!testElement.classList.contains("c2")) { + var createMethod = function(method) { + var original = DOMTokenList.prototype[method]; + + DOMTokenList.prototype[method] = function(token) { + var i, len = arguments.length; + + for (i = 0; i < len; i++) { + token = arguments[i]; + original.call(this, token); + } + }; + }; + createMethod('add'); + createMethod('remove'); + } + + testElement.classList.toggle("c3", false); + + // Polyfill for IE 10 and Firefox <24, where classList.toggle does not + // support the second argument. + if (testElement.classList.contains("c3")) { + var _toggle = DOMTokenList.prototype.toggle; + + DOMTokenList.prototype.toggle = function(token, force) { + if (1 in arguments && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; + + } + + testElement = null; +}()); + +} diff --git a/content/assets/javascripts/docs.js b/content/assets/javascripts/docs.js index 4d894590..100a0f9b 100644 --- a/content/assets/javascripts/docs.js +++ b/content/assets/javascripts/docs.js @@ -1,3 +1,5 @@ +var NAV_INLINE_BREAKPOINT = 1100; + var navtoggle = document.getElementById("docs-nav-toggle"); if (navtoggle) { navtoggle.addEventListener("click", toggleNavigation); @@ -43,14 +45,18 @@ function toggleNavigation() { var sidebarHeight = sidebar.querySelector('ul').getBoundingClientRect().height + 55; + // When we scroll down to the bottom, we don't want the footer covering + // the TOC list (sticky behavior) document.addEventListener('scroll', function() { - if (window.innerWidth < 1099) return; + var isTouchingBottom = false; + if (window.innerWidth >= NAV_INLINE_BREAKPOINT) { + isTouchingBottom = window.scrollY + sidebarHeight >= main[0].offsetHeight; + } - if (window.scrollY + sidebarHeight >= main[0].offsetHeight) { - sidebar.style.position = 'absolute'; + sidebar.classList.toggle('doc-nav-bottom-touching', isTouchingBottom) + if (isTouchingBottom) { sidebar.style.top = (main[0].offsetHeight - sidebarHeight) + 'px'; } else { - sidebar.style.position = ''; sidebar.style.top = ''; } }, { passive : true }); diff --git a/content/assets/stylesheets/stylesheet.scss b/content/assets/stylesheets/stylesheet.scss index 30cf3519..cf0e206f 100644 --- a/content/assets/stylesheets/stylesheet.scss +++ b/content/assets/stylesheets/stylesheet.scss @@ -258,10 +258,6 @@ h6 { .breadcrumbs { font-style: italic; - li { - margin-top: 20px; - } - .last-updated { color: $color-light-gray; font-size: 13px; @@ -270,6 +266,7 @@ h6 { } .header { + z-index: 1; background: $main-background-color; position: relative; width: 100%; diff --git a/content/assets/stylesheets/toc.scss b/content/assets/stylesheets/toc.scss index 4a1ad2fa..852e3522 100644 --- a/content/assets/stylesheets/toc.scss +++ b/content/assets/stylesheets/toc.scss @@ -4,8 +4,22 @@ version: 2 @import "variables"; +.clear { + clear: both; +} + +.doc-nav { + h4 { + padding-bottom: 5px; + } + + li > a { + font-size: 14px; + } +} + // ToC styles -.breadcrumbs + ul, .doc-nav > ul, .quick-nav ul { +.doc-nav > ul { background: $body-background-color; padding: 20px; margin-top: 60px; @@ -20,29 +34,8 @@ version: 2 } } -.doc-nav, .quick-nav { - h4 { - padding-bottom: 5px; - } - - li > a { - font-size: 14px; - } -} - -.clear { - clear: both; -} - // not wide enough to show quick nav and toc @media(max-width:1099px) { - .doc-nav { - margin: 0 auto; - background: $white; - width: inherit; - max-width: 900px; - } - .doc-nav > ul { margin: 0; } @@ -53,10 +46,6 @@ version: 2 margin: 0 auto; max-width: 900px; } - - .quick-nav { - display: none; - } } // wide enough to show toc but not quick nav @@ -78,65 +67,46 @@ version: 2 right: 0; padding-top: 55px; overflow-y: scroll; - height: 100%; - } - - .doc-nav ul ul { - padding-left: 25px; + height: 100vh; } - .doc-nav > ul .quick-nav ul { - margin: 0; + .doc-nav-bottom-touching { + position: absolute; } - .doc-nav, .quick-nav { - width: 25%; + .doc-nav ul ul { + padding-left: 25px; } } @media(min-width:1100px) and (max-width:1600px) { - .quick-nav { - display: none; + .main.class.has-toc { + width: 70vw; + max-width: 70vw; + float: left; } .doc-nav { - width: 30%; - max-width: 30%; + width: 30vw; + max-width: 30vw; } - - .main.class.has-toc { - width: 70%; - max-width: 70%; - float: left; + .doc-nav-bottom-touching { + right: -30vw; } } // wide enough to show quick nav and toc @media(min-width:1601px) { .main.class.has-toc { - width: 50%; - margin-left: 25%; - max-width: 50%; - } - - .quick-nav { - position: fixed; - top: 0; - padding-top: 55px; - left: 0; - overflow-y: scroll; - height: 100%; + width: 50vw; + max-width: 50vw; + margin-left: 25vw; } - .quick-nav ul ul { - padding-bottom: 0; - } - - .quick-nav h4 { - margin-top: 25px; + .doc-nav { + width: 25vw; } - - .quick-nav h4:first-child { - margin-top: 10px; + .doc-nav-bottom-touching { + right: -25vw; } } |