Static table of contents (#701)

* Add showing/hiding submenus, fix sidebar styling, fix bug where includes wouldn't appear in ToC

* Update ToC to highlight last header if page is scrolled to very bottom, fixes #280

* Set HTML title to current h1 section text, see #133

* Fix menu not opening on mobile

* Add back increase toc item height on mobile

* Fix padding bug

* Add back in ToC sliding animation
This commit is contained in:
Robert Lord 2017-02-24 11:57:39 -06:00 committed by GitHub
parent e7f5144e4c
commit 53e2f23e5c
13 changed files with 203 additions and 1698 deletions

View file

@ -7,3 +7,4 @@ gem 'middleman-autoprefixer', '~> 2.7.0'
gem "middleman-sprockets", "~> 4.1.0" gem "middleman-sprockets", "~> 4.1.0"
gem 'rouge', '~> 2.0.5' gem 'rouge', '~> 2.0.5'
gem 'redcarpet', '~> 3.4.0' gem 'redcarpet', '~> 3.4.0'
gem 'nokogiri', '~> 1.6.8'

View file

@ -79,7 +79,10 @@ GEM
middleman-syntax (3.0.0) middleman-syntax (3.0.0)
middleman-core (>= 3.2) middleman-core (>= 3.2)
rouge (~> 2.0) rouge (~> 2.0)
mini_portile2 (2.1.0)
minitest (5.10.1) minitest (5.10.1)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
padrino-helpers (0.13.3.3) padrino-helpers (0.13.3.3)
i18n (~> 0.6, >= 0.6.7) i18n (~> 0.6, >= 0.6.7)
padrino-support (= 0.13.3.3) padrino-support (= 0.13.3.3)
@ -115,8 +118,9 @@ DEPENDENCIES
middleman-autoprefixer (~> 2.7.0) middleman-autoprefixer (~> 2.7.0)
middleman-sprockets (~> 4.1.0) middleman-sprockets (~> 4.1.0)
middleman-syntax (~> 3.0.0) middleman-syntax (~> 3.0.0)
nokogiri (~> 1.6.8)
redcarpet (~> 3.4.0) redcarpet (~> 3.4.0)
rouge (~> 2.0.5) rouge (~> 2.0.5)
BUNDLED WITH BUNDLED WITH
1.14.3 1.14.5

View file

@ -47,3 +47,7 @@ end
# Deploy Configuration # Deploy Configuration
# If you want Middleman to listen on a different port, you can set that below # If you want Middleman to listen on a different port, you can set that below
set :port, 4567 set :port, 4567
helpers do
require './lib/toc_data.rb'
end

30
lib/toc_data.rb Normal file
View file

@ -0,0 +1,30 @@
require 'nokogiri'
def toc_data(page_content)
html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content)
# get a flat list of headers
headers = []
html_doc.css('h1, h2, h3').each do |header|
headers.push({
id: header.attribute('id').to_s,
content: header.content,
level: header.name[1].to_i,
children: []
})
end
[3,2].each do |header_level|
header_to_nest = nil
headers = headers.reject do |header|
if header[:level] == header_level
header_to_nest[:children].push header if header_to_nest
true
else
header_to_nest = header if header[:level] == (header_level - 1)
false
end
end
end
headers
end

View file

@ -1,4 +1,2 @@
//= require ./lib/_energize //= require ./all_nosearch
//= require ./app/_lang
//= require ./app/_search //= require ./app/_search
//= require ./app/_toc

View file

@ -1,3 +1,16 @@
//= require ./lib/_energize //= require ./lib/_energize
//= require ./app/_lang
//= require ./app/_toc //= require ./app/_toc
//= require ./app/_lang
$(function() {
loadToc($('#toc'), '.toc-link', '.toc-list-h2', 10);
setupLanguages($('body').data('languages'));
$('.content').imagesLoaded( function() {
window.recacheHeights();
window.refreshToc();
});
});
window.onpopstate = function() {
activateLanguage(getLanguageFromQueryString());
};

View file

@ -15,13 +15,14 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations License for the specific language governing permissions and limitations
under the License. under the License.
*/ */
(function (global) { ;(function () {
'use strict'; 'use strict';
var languages = []; var languages = [];
global.setupLanguages = setupLanguages; window.setupLanguages = setupLanguages;
global.activateLanguage = activateLanguage; window.activateLanguage = activateLanguage;
window.getLanguageFromQueryString = getLanguageFromQueryString;
function activateLanguage(language) { function activateLanguage(language) {
if (!language) return; if (!language) return;
@ -36,7 +37,7 @@ under the License.
$(".highlight.tab-" + language).show(); $(".highlight.tab-" + language).show();
$(".lang-specific." + language).show(); $(".lang-specific." + language).show();
global.toc.calculateHeights(); window.recacheHeights();
// scroll to the new location of the position // scroll to the new location of the position
if ($(window.location.hash).get(0)) { if ($(window.location.hash).get(0)) {
@ -159,8 +160,5 @@ under the License.
activateLanguage(language); activateLanguage(language);
return false; return false;
}); });
window.onpopstate = function() {
activateLanguage(getLanguageFromQueryString());
};
}); });
})(window); })();

View file

@ -1,7 +1,7 @@
//= require ../lib/_lunr //= require ../lib/_lunr
//= require ../lib/_jquery //= require ../lib/_jquery
//= require ../lib/_jquery.highlight //= require ../lib/_jquery.highlight
(function () { ;(function () {
'use strict'; 'use strict';
var content, searchResults; var content, searchResults;

View file

@ -1,57 +1,106 @@
//= require ../lib/_jquery //= require ../lib/_jquery
//= require ../lib/_jquery_ui
//= require ../lib/_jquery.tocify
//= require ../lib/_imagesloaded.min //= require ../lib/_imagesloaded.min
(function (global) { ;(function () {
'use strict'; 'use strict';
var debounce = function(func, waitTime) {
var timeout = false;
return function() {
if (timeout === false) {
setTimeout(function() {
func();
timeout = false;
}, waitTime);
timeout = true;
}
};
};
var closeToc = function() { var closeToc = function() {
$(".tocify-wrapper").removeClass('open'); $(".tocify-wrapper").removeClass('open');
$("#nav-button").removeClass('open'); $("#nav-button").removeClass('open');
}; };
var makeToc = function() { function loadToc($toc, tocLinkSelector, tocListSelector, scrollOffset) {
global.toc = $("#toc").tocify({ var headerHeights = {};
selectors: 'h1, h2', var pageHeight = 0;
extendPage: false, var windowHeight = 0;
theme: 'none', var originalTitle = document.title;
smoothScroll: false,
showEffectSpeed: 0, var recacheHeights = function() {
hideEffectSpeed: 180, headerHeights = {};
ignoreSelector: '.toc-ignore', pageHeight = $(document).height();
highlightOffset: 60, windowHeight = $(window).height();
scrollTo: -1,
scrollHistory: true, $toc.find(tocLinkSelector).each(function() {
hashGenerator: function (text, element) { var targetId = $(this).attr('href');
return element.prop('id'); if (targetId[0] === "#") {
headerHeights[targetId] = $(targetId).offset().top;
}
});
};
var refreshToc = function() {
var currentTop = $(document).scrollTop() + scrollOffset;
if (currentTop + windowHeight >= pageHeight) {
// at bottom of page, so just select last header by making currentTop very large
// this fixes the problem where the last header won't ever show as active if its content
// is shorter than the window height
currentTop = pageHeight + 1000;
} }
}).data('toc-tocify');
$("#nav-button").click(function() { var best = null;
$(".tocify-wrapper").toggleClass('open'); for (var name in headerHeights) {
$("#nav-button").toggleClass('open'); if ((headerHeights[name] < currentTop && headerHeights[name] > headerHeights[best]) || best === null) {
return false; best = name;
}); }
}
$(".page-wrapper").click(closeToc); var $best = $toc.find("[href='" + best + "']").first();
$(".tocify-item").click(closeToc); if (!$best.hasClass("active")) {
}; $toc.find(".active").removeClass("active");
$best.addClass("active");
$best.parents(tocListSelector).addClass("active");
$best.siblings(tocListSelector).addClass("active");
$toc.find(tocListSelector).filter(":not(.active)").slideUp(150);
$toc.find(tocListSelector).filter(".active").slideDown(150);
if (window.history.pushState) {
window.history.pushState(null, "", best);
}
// TODO remove classnames
document.title = $best.data("title") + " " + originalTitle;
}
};
// Hack to make already open sections to start opened, var makeToc = function() {
// instead of displaying an ugly animation recacheHeights();
function animate() { refreshToc();
setTimeout(function() {
toc.setOption('showEffectSpeed', 180); $("#nav-button").click(function() {
}, 50); $(".toc-wrapper").toggleClass('open');
$("#nav-button").toggleClass('open');
return false;
});
$(".page-wrapper").click(closeToc);
$(".tocify-item").click(closeToc);
// reload immediately after scrolling on toc click
$toc.find(tocLinkSelector).click(function() {
setTimeout(function() {
refreshToc();
}, 0);
});
$(window).scroll(debounce(refreshToc, 200));
$(window).resize(debounce(recacheHeights, 200));
};
makeToc();
window.recacheHeights = recacheHeights;
window.refreshToc = refreshToc;
} }
$(function() { window.loadToc = loadToc;
makeToc(); })();
animate();
setupLanguages($('body').data('languages'));
$('.content').imagesLoaded( function() {
global.toc.calculateHeights();
});
});
})(window);

File diff suppressed because it is too large Load diff

View file

@ -1,566 +0,0 @@
/*! jQuery UI - v1.11.3 - 2015-02-12
* http://jqueryui.com
* Includes: widget.js
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([ "jquery" ], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {
/*!
* jQuery UI Widget 1.11.3
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/jQuery.widget/
*/
var widget_uuid = 0,
widget_slice = Array.prototype.slice;
$.cleanData = (function( orig ) {
return function( elems ) {
var events, elem, i;
for ( i = 0; (elem = elems[i]) != null; i++ ) {
try {
// Only trigger remove when necessary to save time
events = $._data( elem, "events" );
if ( events && events.remove ) {
$( elem ).triggerHandler( "remove" );
}
// http://bugs.jquery.com/ticket/8235
} catch ( e ) {}
}
orig( elems );
};
})( $.cleanData );
$.widget = function( name, base, prototype ) {
var fullName, existingConstructor, constructor, basePrototype,
// proxiedPrototype allows the provided prototype to remain unmodified
// so that it can be used as a mixin for multiple widgets (#8876)
proxiedPrototype = {},
namespace = name.split( "." )[ 0 ];
name = name.split( "." )[ 1 ];
fullName = namespace + "-" + name;
if ( !prototype ) {
prototype = base;
base = $.Widget;
}
// create selector for plugin
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
return !!$.data( elem, fullName );
};
$[ namespace ] = $[ namespace ] || {};
existingConstructor = $[ namespace ][ name ];
constructor = $[ namespace ][ name ] = function( options, element ) {
// allow instantiation without "new" keyword
if ( !this._createWidget ) {
return new constructor( options, element );
}
// allow instantiation without initializing for simple inheritance
// must use "new" keyword (the code above always passes args)
if ( arguments.length ) {
this._createWidget( options, element );
}
};
// extend with the existing constructor to carry over any static properties
$.extend( constructor, existingConstructor, {
version: prototype.version,
// copy the object used to create the prototype in case we need to
// redefine the widget later
_proto: $.extend( {}, prototype ),
// track widgets that inherit from this widget in case this widget is
// redefined after a widget inherits from it
_childConstructors: []
});
basePrototype = new base();
// we need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
basePrototype.options = $.widget.extend( {}, basePrototype.options );
$.each( prototype, function( prop, value ) {
if ( !$.isFunction( value ) ) {
proxiedPrototype[ prop ] = value;
return;
}
proxiedPrototype[ prop ] = (function() {
var _super = function() {
return base.prototype[ prop ].apply( this, arguments );
},
_superApply = function( args ) {
return base.prototype[ prop ].apply( this, args );
};
return function() {
var __super = this._super,
__superApply = this._superApply,
returnValue;
this._super = _super;
this._superApply = _superApply;
returnValue = value.apply( this, arguments );
this._super = __super;
this._superApply = __superApply;
return returnValue;
};
})();
});
constructor.prototype = $.widget.extend( basePrototype, {
// TODO: remove support for widgetEventPrefix
// always use the name + a colon as the prefix, e.g., draggable:start
// don't prefix for widgets that aren't DOM-based
widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
}, proxiedPrototype, {
constructor: constructor,
namespace: namespace,
widgetName: name,
widgetFullName: fullName
});
// If this widget is being redefined then we need to find all widgets that
// are inheriting from it and redefine all of them so that they inherit from
// the new version of this widget. We're essentially trying to replace one
// level in the prototype chain.
if ( existingConstructor ) {
$.each( existingConstructor._childConstructors, function( i, child ) {
var childPrototype = child.prototype;
// redefine the child widget using the same prototype that was
// originally used, but inherit from the new version of the base
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
});
// remove the list of existing child constructors from the old constructor
// so the old child constructors can be garbage collected
delete existingConstructor._childConstructors;
} else {
base._childConstructors.push( constructor );
}
$.widget.bridge( name, constructor );
return constructor;
};
$.widget.extend = function( target ) {
var input = widget_slice.call( arguments, 1 ),
inputIndex = 0,
inputLength = input.length,
key,
value;
for ( ; inputIndex < inputLength; inputIndex++ ) {
for ( key in input[ inputIndex ] ) {
value = input[ inputIndex ][ key ];
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
// Clone objects
if ( $.isPlainObject( value ) ) {
target[ key ] = $.isPlainObject( target[ key ] ) ?
$.widget.extend( {}, target[ key ], value ) :
// Don't extend strings, arrays, etc. with objects
$.widget.extend( {}, value );
// Copy everything else by reference
} else {
target[ key ] = value;
}
}
}
}
return target;
};
$.widget.bridge = function( name, object ) {
var fullName = object.prototype.widgetFullName || name;
$.fn[ name ] = function( options ) {
var isMethodCall = typeof options === "string",
args = widget_slice.call( arguments, 1 ),
returnValue = this;
if ( isMethodCall ) {
this.each(function() {
var methodValue,
instance = $.data( this, fullName );
if ( options === "instance" ) {
returnValue = instance;
return false;
}
if ( !instance ) {
return $.error( "cannot call methods on " + name + " prior to initialization; " +
"attempted to call method '" + options + "'" );
}
if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
return $.error( "no such method '" + options + "' for " + name + " widget instance" );
}
methodValue = instance[ options ].apply( instance, args );
if ( methodValue !== instance && methodValue !== undefined ) {
returnValue = methodValue && methodValue.jquery ?
returnValue.pushStack( methodValue.get() ) :
methodValue;
return false;
}
});
} else {
// Allow multiple hashes to be passed on init
if ( args.length ) {
options = $.widget.extend.apply( null, [ options ].concat(args) );
}
this.each(function() {
var instance = $.data( this, fullName );
if ( instance ) {
instance.option( options || {} );
if ( instance._init ) {
instance._init();
}
} else {
$.data( this, fullName, new object( options, this ) );
}
});
}
return returnValue;
};
};
$.Widget = function( /* options, element */ ) {};
$.Widget._childConstructors = [];
$.Widget.prototype = {
widgetName: "widget",
widgetEventPrefix: "",
defaultElement: "<div>",
options: {
disabled: false,
// callbacks
create: null
},
_createWidget: function( options, element ) {
element = $( element || this.defaultElement || this )[ 0 ];
this.element = $( element );
this.uuid = widget_uuid++;
this.eventNamespace = "." + this.widgetName + this.uuid;
this.bindings = $();
this.hoverable = $();
this.focusable = $();
if ( element !== this ) {
$.data( element, this.widgetFullName, this );
this._on( true, this.element, {
remove: function( event ) {
if ( event.target === element ) {
this.destroy();
}
}
});
this.document = $( element.style ?
// element within the document
element.ownerDocument :
// element is window or document
element.document || element );
this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
}
this.options = $.widget.extend( {},
this.options,
this._getCreateOptions(),
options );
this._create();
this._trigger( "create", null, this._getCreateEventData() );
this._init();
},
_getCreateOptions: $.noop,
_getCreateEventData: $.noop,
_create: $.noop,
_init: $.noop,
destroy: function() {
this._destroy();
// we can probably remove the unbind calls in 2.0
// all event bindings should go through this._on()
this.element
.unbind( this.eventNamespace )
.removeData( this.widgetFullName )
// support: jquery <1.6.3
// http://bugs.jquery.com/ticket/9413
.removeData( $.camelCase( this.widgetFullName ) );
this.widget()
.unbind( this.eventNamespace )
.removeAttr( "aria-disabled" )
.removeClass(
this.widgetFullName + "-disabled " +
"ui-state-disabled" );
// clean up events and states
this.bindings.unbind( this.eventNamespace );
this.hoverable.removeClass( "ui-state-hover" );
this.focusable.removeClass( "ui-state-focus" );
},
_destroy: $.noop,
widget: function() {
return this.element;
},
option: function( key, value ) {
var options = key,
parts,
curOption,
i;
if ( arguments.length === 0 ) {
// don't return a reference to the internal hash
return $.widget.extend( {}, this.options );
}
if ( typeof key === "string" ) {
// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
options = {};
parts = key.split( "." );
key = parts.shift();
if ( parts.length ) {
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
for ( i = 0; i < parts.length - 1; i++ ) {
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
curOption = curOption[ parts[ i ] ];
}
key = parts.pop();
if ( arguments.length === 1 ) {
return curOption[ key ] === undefined ? null : curOption[ key ];
}
curOption[ key ] = value;
} else {
if ( arguments.length === 1 ) {
return this.options[ key ] === undefined ? null : this.options[ key ];
}
options[ key ] = value;
}
}
this._setOptions( options );
return this;
},
_setOptions: function( options ) {
var key;
for ( key in options ) {
this._setOption( key, options[ key ] );
}
return this;
},
_setOption: function( key, value ) {
this.options[ key ] = value;
if ( key === "disabled" ) {
this.widget()
.toggleClass( this.widgetFullName + "-disabled", !!value );
// If the widget is becoming disabled, then nothing is interactive
if ( value ) {
this.hoverable.removeClass( "ui-state-hover" );
this.focusable.removeClass( "ui-state-focus" );
}
}
return this;
},
enable: function() {
return this._setOptions({ disabled: false });
},
disable: function() {
return this._setOptions({ disabled: true });
},
_on: function( suppressDisabledCheck, element, handlers ) {
var delegateElement,
instance = this;
// no suppressDisabledCheck flag, shuffle arguments
if ( typeof suppressDisabledCheck !== "boolean" ) {
handlers = element;
element = suppressDisabledCheck;
suppressDisabledCheck = false;
}
// no element argument, shuffle and use this.element
if ( !handlers ) {
handlers = element;
element = this.element;
delegateElement = this.widget();
} else {
element = delegateElement = $( element );
this.bindings = this.bindings.add( element );
}
$.each( handlers, function( event, handler ) {
function handlerProxy() {
// allow widgets to customize the disabled handling
// - disabled as an array instead of boolean
// - disabled class as method for disabling individual parts
if ( !suppressDisabledCheck &&
( instance.options.disabled === true ||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
return;
}
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
// copy the guid so direct unbinding works
if ( typeof handler !== "string" ) {
handlerProxy.guid = handler.guid =
handler.guid || handlerProxy.guid || $.guid++;
}
var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
eventName = match[1] + instance.eventNamespace,
selector = match[2];
if ( selector ) {
delegateElement.delegate( selector, eventName, handlerProxy );
} else {
element.bind( eventName, handlerProxy );
}
});
},
_off: function( element, eventName ) {
eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
this.eventNamespace;
element.unbind( eventName ).undelegate( eventName );
// Clear the stack to avoid memory leaks (#10056)
this.bindings = $( this.bindings.not( element ).get() );
this.focusable = $( this.focusable.not( element ).get() );
this.hoverable = $( this.hoverable.not( element ).get() );
},
_delay: function( handler, delay ) {
function handlerProxy() {
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
var instance = this;
return setTimeout( handlerProxy, delay || 0 );
},
_hoverable: function( element ) {
this.hoverable = this.hoverable.add( element );
this._on( element, {
mouseenter: function( event ) {
$( event.currentTarget ).addClass( "ui-state-hover" );
},
mouseleave: function( event ) {
$( event.currentTarget ).removeClass( "ui-state-hover" );
}
});
},
_focusable: function( element ) {
this.focusable = this.focusable.add( element );
this._on( element, {
focusin: function( event ) {
$( event.currentTarget ).addClass( "ui-state-focus" );
},
focusout: function( event ) {
$( event.currentTarget ).removeClass( "ui-state-focus" );
}
});
},
_trigger: function( type, event, data ) {
var prop, orig,
callback = this.options[ type ];
data = data || {};
event = $.Event( event );
event.type = ( type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type ).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[ 0 ];
// copy original event properties over to the new event
orig = event.originalEvent;
if ( orig ) {
for ( prop in orig ) {
if ( !( prop in event ) ) {
event[ prop ] = orig[ prop ];
}
}
}
this.element.trigger( event, data );
return !( $.isFunction( callback ) &&
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
event.isDefaultPrevented() );
}
};
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
if ( typeof options === "string" ) {
options = { effect: options };
}
var hasOptions,
effectName = !options ?
method :
options === true || typeof options === "number" ?
defaultEffect :
options.effect || defaultEffect;
options = options || {};
if ( typeof options === "number" ) {
options = { duration: options };
}
hasOptions = !$.isEmptyObject( options );
options.complete = callback;
if ( options.delay ) {
element.delay( options.delay );
}
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
element[ method ]( options );
} else if ( effectName !== method && element[ effectName ] ) {
element[ effectName ]( options.duration, options.easing, callback );
} else {
element.queue(function( next ) {
$( this )[ method ]();
if ( callback ) {
callback.call( element[ 0 ] );
}
next();
});
}
};
});
var widget = $.widget;
}));

View file

@ -14,6 +14,15 @@ License for the specific language governing permissions and limitations
under the License. under the License.
%> %>
<% language_tabs = current_page.data.language_tabs || [] %> <% language_tabs = current_page.data.language_tabs || [] %>
<% page_content = yield %>
<%
if current_page.data.includes
current_page.data.includes.each do |include|
page_content += partial("includes/#{include}")
end
end
%>
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
@ -41,7 +50,7 @@ under the License.
<%= image_tag('navbar.png') %> <%= image_tag('navbar.png') %>
</span> </span>
</a> </a>
<div class="tocify-wrapper"> <div class="toc-wrapper">
<%= image_tag "logo.png", class: 'logo' %> <%= image_tag "logo.png", class: 'logo' %>
<% if language_tabs.any? %> <% if language_tabs.any? %>
<div class="lang-selector"> <div class="lang-selector">
@ -60,7 +69,21 @@ under the License.
</div> </div>
<ul class="search-results"></ul> <ul class="search-results"></ul>
<% end %> <% end %>
<div id="toc"> <div id="toc" class="toc-list-h1">
<% toc_data(page_content).each do |h1| %>
<li>
<a href="#<%= h1[:id] %>" class="toc-h1 toc-link" data-title="<%= h1[:content] %>"><%= h1[:content] %></a>
<% if h1[:children].length > 0 %>
<ul class="toc-list-h2">
<% h1[:children].each do |h2| %>
<li>
<a href="#<%= h2[:id] %>" class="toc-h2 toc-link" data-title="<%= h1[:content] %>"><%= h2[:content] %></a>
</li>
<% end %>
</ul>
<% end %>
</li>
<% end %>
</div> </div>
<% if current_page.data.toc_footers %> <% if current_page.data.toc_footers %>
<ul class="toc-footer"> <ul class="toc-footer">
@ -73,10 +96,7 @@ under the License.
<div class="page-wrapper"> <div class="page-wrapper">
<div class="dark-box"></div> <div class="dark-box"></div>
<div class="content"> <div class="content">
<%= yield %> <%= page_content %>
<% current_page.data.includes && current_page.data.includes.each do |include| %>
<%= partial "includes/#{include}" %>
<% end %>
</div> </div>
<div class="dark-box"> <div class="dark-box">
<% if language_tabs.any? %> <% if language_tabs.any? %>

View file

@ -55,7 +55,7 @@ html, body {
$nav-subitem-bg; $nav-subitem-bg;
} }
.tocify-wrapper { .toc-wrapper {
transition: left 0.3s ease-in-out; transition: left 0.3s ease-in-out;
overflow-y: auto; overflow-y: auto;
@ -146,14 +146,6 @@ html, body {
} }
.tocify-item>a, .toc-footer li {
padding: 0 $nav-padding 0 $nav-padding;
display: block;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
// The Table of Contents is composed of multiple nested // The Table of Contents is composed of multiple nested
// unordered lists. These styles remove the default // unordered lists. These styles remove the default
// styling of an unordered list because it is ugly. // styling of an unordered list because it is ugly.
@ -164,30 +156,17 @@ html, body {
line-height: 28px; line-height: 28px;
} }
li {
color: $nav-text;
transition-property: background;
transition-timing-function: linear;
transition-duration: 230ms;
}
// This is the currently selected ToC entry // This is the currently selected ToC entry
.tocify-focus { .toc-link.active {
box-shadow: 0px 1px 0px $nav-active-shadow; box-shadow: 0px 1px 0px $nav-active-shadow;
background-color: $nav-active-bg; background-color: $nav-active-bg;
color: $nav-active-text; color: $nav-active-text;
} }
// Subheaders are the submenus that slide open .toc-list-h2 {
// in the table of contents. display: none;
.tocify-subheader {
display: none; // tocify will override this when needed
background-color: $nav-subitem-bg; background-color: $nav-subitem-bg;
font-weight: 500; font-weight: 500;
.tocify-item>a {
padding-left: $nav-padding + $nav-indent;
font-size: 12px;
}
// for embossed look: // for embossed look:
@include embossed-bg; @include embossed-bg;
@ -196,6 +175,11 @@ html, body {
} }
} }
.toc-h2 {
padding-left: $nav-padding + $nav-indent;
font-size: 12px;
}
.toc-footer { .toc-footer {
padding: 1em 0; padding: 1em 0;
margin-top: 1em; margin-top: 1em;
@ -216,7 +200,19 @@ html, body {
text-decoration: none; text-decoration: none;
} }
} }
}
.toc-link, .toc-footer li {
padding: 0 $nav-padding 0 $nav-padding;
display: block;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-decoration: none;
color: #fff;
transition-property: background;
transition-timing-function: linear;
transition-duration: 130ms;
} }
// button to show navigation on mobile devices // button to show navigation on mobile devices
@ -571,7 +567,7 @@ html, body {
// There are also a couple styles disperesed // There are also a couple styles disperesed
@media (max-width: $tablet-width) { @media (max-width: $tablet-width) {
.tocify-wrapper { .toc-wrapper {
left: -$nav-width; left: -$nav-width;
&.open { &.open {
@ -587,7 +583,7 @@ html, body {
display: block; display: block;
} }
.tocify-wrapper .tocify-item > a { .toc-link {
padding-top: 0.3em; padding-top: 0.3em;
padding-bottom: 0.3em; padding-bottom: 0.3em;
} }
@ -602,7 +598,7 @@ html, body {
margin-right: 0; margin-right: 0;
} }
.tocify-wrapper .lang-selector { .toc-wrapper .lang-selector {
display: block; display: block;
} }