diff options
author | Achilleas Pipinellis <axil@gitlab.com> | 2018-02-07 15:30:26 +0300 |
---|---|---|
committer | Achilleas Pipinellis <axil@gitlab.com> | 2018-02-07 15:30:26 +0300 |
commit | 29a8cf3c2a6355955fbc17be8c2c84d9fd17dc25 (patch) | |
tree | 29a14f0af67f8f0c414fb4800b675c63b0baaeff /content | |
parent | 1c38d9811df4ba39ffb572a45ed7c3fa87e1b395 (diff) | |
parent | 89883d5e65e0f17c27f8dae41c32848324d7b419 (diff) |
Merge branch 'article-layout-1' into 'master'
Article layout
Closes #157
See merge request gitlab-com/gitlab-docs!182
Diffstat (limited to 'content')
-rw-r--r-- | content/assets/javascripts/classlist-polyfill.js | 240 | ||||
-rw-r--r-- | content/assets/javascripts/docs.js | 38 | ||||
-rw-r--r-- | content/assets/stylesheets/_variables.scss | 2 | ||||
-rw-r--r-- | content/assets/stylesheets/stylesheet.scss | 38 | ||||
-rw-r--r-- | content/assets/stylesheets/toc.scss | 101 | ||||
-rw-r--r-- | content/index.erb | 2 |
6 files changed, 326 insertions, 95 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 ae70aad0..05e8ab35 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); @@ -10,22 +12,22 @@ function toggleNavigation() { // move document nav to sidebar (function() { - var nav = document.querySelectorAll('.breadcrumbs +ul'); - var main = document.querySelectorAll('.main.class'); + var tocList = document.querySelector('.js-article-content > ul:first-child'); + var main = document.querySelector('.js-main-wrapper'); // if the document has a top level nav - if(nav[0]) { + if(tocList) { // append to the sidebar var sidebar = document.getElementById('doc-nav'); if(sidebar) { // if there is one h1 in the documentation - if(nav[0].children.length == 1) { + if(tocList.children.length == 1) { // if there is a nested ul after the first anchor - if(nav[0].children[0].children.length > 1) { - var menu = nav[0].children[0].children[1]; + if(tocList.children[0].children.length > 1) { + var menu = tocList.children[0].children[1]; var footnotes = menu.querySelector('.footnotes'); if (footnotes) { @@ -43,33 +45,37 @@ 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.offsetHeight; + } - if (window.scrollY + sidebarHeight >= main[0].offsetHeight) { - sidebar.style.position = 'absolute'; - sidebar.style.top = (main[0].offsetHeight - sidebarHeight) + 'px'; + sidebar.classList.toggle('doc-nav-bottom-touching', isTouchingBottom) + if (isTouchingBottom) { + sidebar.style.top = (main.offsetHeight - sidebarHeight) + 'px'; } else { - sidebar.style.position = ''; sidebar.style.top = ''; } }, { passive : true }); } // remove what is left of the old navigation - nav[0].remove() + tocList.remove() } else { - nav[0].remove() + tocList.remove() } } // main content has-toc - if (main[0] && main[0].classList) { - main[0].classList.add('has-toc'); + if (main && main.classList) { + main.classList.add('has-toc'); } else { - main[0].className += ' has-toc'; + main.className += ' has-toc'; } } })(); diff --git a/content/assets/stylesheets/_variables.scss b/content/assets/stylesheets/_variables.scss index 978bf2ce..d3351399 100644 --- a/content/assets/stylesheets/_variables.scss +++ b/content/assets/stylesheets/_variables.scss @@ -229,3 +229,5 @@ $facebook-bg: #3b5998; $twitter-bg: #55acee; $google-bg: #dd4b39; $linkedin-bg: #007bb5; + +$article-metadata: rgb(110, 110, 110); diff --git a/content/assets/stylesheets/stylesheet.scss b/content/assets/stylesheets/stylesheet.scss index b8635318..cf0e206f 100644 --- a/content/assets/stylesheets/stylesheet.scss +++ b/content/assets/stylesheets/stylesheet.scss @@ -178,7 +178,9 @@ h6 { .edit-on { text-align: center; - + border-top: 1px solid $hr-border-color; + padding-top: 20px; + margin-top: 15px; a { color: $h1-border-bottom; &::before { @@ -221,7 +223,7 @@ h6 { } p { - margin: 20px 0; + margin: 7px 0; line-height: 22px; } @@ -254,11 +256,7 @@ h6 { } .breadcrumbs { - margin: 0; - - li { - margin-top: 20px; - } + font-style: italic; .last-updated { color: $color-light-gray; @@ -268,6 +266,7 @@ h6 { } .header { + z-index: 1; background: $main-background-color; position: relative; width: 100%; @@ -414,6 +413,26 @@ h6 { } } +// Articles layout +.article-metadata { + color: $article-metadata; + display: block; + order: -1; + font-style: italic; + margin-bottom: 15px; +} + +.article-content { + > h1:first-of-type { + display: none; + } +} + +// Override bootstrap's alert color +.alert { + color: inherit; +} + // Home page // .landing { @@ -559,11 +578,6 @@ h6 { visibility: hidden; } -// Override bootstrap's alert color -.alert { - color: inherit; -} - // // Algolia search for mobile // https://github.com/algolia/docsearch/issues/181#issuecomment-301730219 diff --git a/content/assets/stylesheets/toc.scss b/content/assets/stylesheets/toc.scss index 040f6a48..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,30 +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; - padding: 40px 40px 0; - background: $white; - width: inherit; - max-width: 900px; - } - .doc-nav > ul { margin: 0; } @@ -54,10 +46,6 @@ version: 2 margin: 0 auto; max-width: 900px; } - - .quick-nav { - display: none; - } } // wide enough to show toc but not quick nav @@ -79,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; } } diff --git a/content/index.erb b/content/index.erb index 527f62fc..b63b494c 100644 --- a/content/index.erb +++ b/content/index.erb @@ -4,7 +4,7 @@ title: GitLab Documentation <ul class="topics" itemscope itemtype="http://www.schema.org/SiteNavigationElement"> <% @config[:products].each do |name, product| %> - <% if product[:expose] == true %> + <% if product[:expose] == true && @items["/#{product[:slug]}/#{product[:index_file]}"] %> <li class="topic-<%= product[:slug] %>"> <a href="<%= @items["/#{product[:slug]}/#{product[:index_file]}"].path %>" itemprop="url"> <div class="wrapper"> |