Skip to content

Commit 5646a5b

Browse files
authored
Merge pull request #1031 from jcb91/ttoc
[toc2] make toc entries collapsible
2 parents 3801417 + 973643e commit 5646a5b

File tree

9 files changed

+231
-165
lines changed

9 files changed

+231
-165
lines changed

src/jupyter_contrib_nbextensions/nbextensions/collapsible_headings/collapsible_headings.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,11 @@ Parameters:
9090
description: "By default, selecting a whole section also expands the section to reveal its last cell. Set this option to false to disable the expansion."
9191
input_type: checkbox
9292
default: true
93+
94+
- name: collapsible_headings.collapse_to_match_toc
95+
description: |
96+
Collapse/uncollapse notebook sections when the ToC2 nbextension is used to
97+
collapse/uncollapse sections in the table of contents. For the inverse
98+
behaviour, see ToC2's configuration
99+
input_type: checkbox
100+
default: false

src/jupyter_contrib_nbextensions/nbextensions/collapsible_headings/main.js

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
show_section_brackets : false,
4242
section_bracket_width : 10,
4343
show_ellipsis : true,
44-
select_reveals : true
44+
select_reveals : true,
45+
collapse_to_match_toc: false,
4546
};
4647

4748
// ------------------------------------------------------------------------
@@ -50,6 +51,21 @@
5051
// It is declared here to allow us to keep logic for live/nonlive functions
5152
// together.
5253
var Jupyter;
54+
// similarly, in a live notebook, events is the Jupyter global events
55+
// object, but in a non-live notebook, we must construct our own version
56+
var events;
57+
try {
58+
events = require('base/js/events');
59+
}
60+
catch (err) {
61+
// in non-live notebook, there's no events structure, so we make our own
62+
if (window.events === undefined) {
63+
var Events = function () {};
64+
window.events = $([new Events()]);
65+
}
66+
events = window.events;
67+
}
68+
5369
// global flag denoting whether we're in a live notebook or exported html.
5470
// In a live notebook we operate on Cell instances, in exported html we
5571
// operate on jQuery collections of '.cell' elements
@@ -486,7 +502,7 @@
486502
*
487503
* @param {Object} cell Cell instance or jQuery collection of '.cell' elements
488504
*/
489-
function toggle_heading (cell, set_collapsed) {
505+
function toggle_heading (cell, set_collapsed, trigger_event) {
490506
if (is_heading(cell)) {
491507
if (set_collapsed === undefined) {
492508
set_collapsed = !_is_collapsed(cell);
@@ -495,6 +511,9 @@
495511
update_heading_cell_status(cell);
496512
update_collapsed_headings(params.show_section_brackets ? undefined : cell);
497513
console.log(log_prefix, set_collapsed ? 'collapsed' : 'expanded', 'cell', _find_cell_index(cell));
514+
if (trigger_event !== false) {
515+
events.trigger((set_collapsed ? '' : 'un') + 'collapse.CollapsibleHeading', {cell: cell});
516+
}
498517
}
499518
}
500519

@@ -796,31 +815,6 @@
796815
Jupyter.notebook.edit_mode();
797816
}
798817

799-
function toc2_callback (evt) {
800-
// evt.target is what was clicked, not what the handler was attached to
801-
var toc_link = $(evt.target).closest('a');
802-
var href = toc_link.attr('href');
803-
href = href.slice(href.indexOf('#') + 1); // remove #
804-
// for toc2's cell-toc links, we use the data-toc-modified-id attr
805-
var toc_mod_href = toc_link.attr('data-toc-modified-id');
806-
807-
// jquery doesn't cope with $(href) or $('a[href=' + href + ']')
808-
// if href contains periods or other unusual characters
809-
var $anchor = $(document.getElementById(toc_mod_href));
810-
if ($anchor.length < 1) {
811-
// we didn't find the toc-modified id, so use the regular id
812-
$anchor = $(document.getElementById(href));
813-
}
814-
if ($anchor.length < 1) {
815-
return;
816-
}
817-
var cell_index = $anchor.closest('.cell').index();
818-
819-
reveal_cell_by_index(cell_index);
820-
// scroll link into view once animation is complete
821-
setTimeout(function () { imitate_hash_click($anchor); }, 400);
822-
}
823-
824818
function refresh_all_headings () {
825819
var cells = _get_cells();
826820
for (var ii=0; ii < cells.length; ii++) {
@@ -832,6 +826,9 @@
832826
function set_collapsible_headings_options (options) {
833827
// options may be undefined here, but it's still handled ok by $.extend
834828
$.extend(true, params, options);
829+
// bind/unbind toc-collapse handler
830+
events[params.collapse_to_match_toc ? 'on' : 'off']('collapse.Toc uncollapse.Toc', callback_toc_collapse);
831+
return params;
835832
}
836833

837834
function add_buttons_and_shortcuts () {
@@ -893,6 +890,11 @@
893890
}
894891
}
895892

893+
var callback_toc_collapse = function (evt, data) {
894+
// use trigger_event false to avoid re-triggering toc2
895+
toggle_heading(data.cell, evt.type.indexOf('un') < 0, false);
896+
}
897+
896898
/**
897899
* Return a promise which resolves once event handlers have been bound
898900
*
@@ -997,10 +999,6 @@
997999
})
9981000
.appendTo('head');
9991001

1000-
// register toc2 callback - see
1001-
// https://github.com/ipython-contrib/jupyter_contrib_nbextensions/issues/609
1002-
$(document).on('click', '.toc-item a', toc2_callback);
1003-
10041002
// ensure Jupyter module is defined before proceeding further
10051003
new Promise(function (resolve, reject) {
10061004
require(['base/js/namespace'], function (Jupyter_mod) {

src/jupyter_contrib_nbextensions/nbextensions/collapsible_headings/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ the nbextensions config page:
4040
indicating hidden content (disabled by default)
4141
* A toolbar button to collapse the nearest heading to the curently selected
4242
cell (disabled by default)
43+
* Collapse/uncollapse sections when ToC2 sections are collapsed/uncollapsed
4344
* A toolbar button to collapse/uncollapse all headings (disabled by default)
4445

4546

src/jupyter_contrib_nbextensions/nbextensions/toc2/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ The initial configuration can be given using the IPython-contrib nbextensions fa
4343
- Skipping h1 headers, useful if you want to use h1 as unnumbered notebook title (default: false)
4444
- Customization of highlighting the title of currently selected/running sections.
4545
- Customization of background, fonts, border and highlighting colors in the toc window and navigation menus (as third demo).
46+
- Collapse/uncollapse ToC2 sections when collapsible_headings is used to collapse/uncollapse notebook sections (default: false).
4647

4748
The differents states and position of the floating window have reasonable defaults and can be modfied per notebook).
4849

@@ -116,3 +117,4 @@ This option requires the IPython kernel and is not present with other kernels.
116117
- Support customization of background, fonts, border and highlighting colors in the toc window and navigation menus with PR [#969](https://github.com/ipython-contrib/jupyter_contrib_nbextensions/pull/969)
117118
- @jfbercher, @louisabraham, @jcb91 July 2017. Add support for skipping h1
118119
headings, enabling their use as unnumbered notebook titles
120+
- @jcb91 with minor contributions by @jfbercher. August 2017. Make toc entries collapsible #1031 with optional synchronization with `collapsible_headings` + some small other tweaks.

src/jupyter_contrib_nbextensions/nbextensions/toc2/main.css

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
/*background color for links when you mouse over it
55
This style is used on export, but can be overwritted in the livenotebook
66
by selecting a color in the nbextension-configurator*/
7-
#toc-level0 li > a:hover { display: block; background-color: #DAA520}
7+
8+
9+
10+
#toc-wrapper li > span:hover {
11+
background-color: #DAA520;
12+
}
813

914
#toc-level0 a {
10-
/*color: #333333;*/ /* now specified in js */
15+
color: #333333; /* default - color also loaded from notebook config */
1116
text-decoration: none;
1217
}
13-
#navigate_menu li a:hover {background-color: #f1f1f1}
18+
#navigate_menu li > span:hover {background-color: #f1f1f1}
1419

1520

1621
/* Move menus and tooolbar to the left, following @Kevin-McIsaac suggestion
@@ -36,13 +41,13 @@ padding-left: 20px;
3641
}
3742

3843
#navigate_menu li {
39-
padding-left: 2px;
44+
padding-left: 0px;
4045
clear: both;
4146
list-style-type: none;
4247
}
4348

44-
#navigate_menu-level0 {padding-left: 10px;}
45-
#navigate_menu-level0 ul {padding-left: 10px;}
49+
#navigate_menu-level0 {padding-left: 0px;}
50+
#navigate_menu-level0 ul {padding-left: 0px;}
4651

4752

4853
.toc {
@@ -65,7 +70,7 @@ padding-left: 20px;
6570
display: block;
6671
}
6772

68-
#toc ul.toc-item {
73+
.toc ul.toc-item {
6974
list-style-type: none;
7075
padding: 0;
7176
margin: 0;
@@ -142,13 +147,21 @@ padding-left: 20px;
142147

143148

144149
/* don't waste so much screen space... */
145-
#toc-wrapper .toc-item{
146-
padding-left: 20px;
147-
}
148150

149-
#toc-wrapper .toc-item .toc-item{
150-
padding-left: 10px;
151+
/*
152+
.toc .toc-item .toc-item {
153+
padding-left: 1em;
151154
}
155+
*/
156+
157+
.toc-item li { margin:0; padding:0; color:black }
158+
.toc-item li > span { display:block }
159+
.toc-item li > span { padding-left:0em }
160+
.toc-item li li > span { padding-left:1em }
161+
.toc-item li li li > span { padding-left:2em }
162+
.toc-item li li li li > span { padding-left:3em }
163+
.toc-item li li li li li > span { padding-left:4em }
164+
.toc-item li li li li li li > span { padding-left:5em }
152165

153166
.toc-item-num {
154167
font-style: normal;
@@ -167,11 +180,6 @@ and updated using the nbextension-configurator
167180
.toc-item-highlight-execute {background-color: red}
168181
.toc-item-highlight-execute.toc-item-highlight-select {background-color: Gold} */
169182

170-
.lev1 {margin-left: 80px}
171-
.lev2 {margin-left: 100px}
172-
.lev3 {margin-left: 120px}
173-
.lev4 {margin-left: 140px}
174-
.lev5 {margin-left: 160px}
175-
.lev6 {margin-left: 180px}
176-
.lev7 {margin-left: 200px}
177-
.lev8 {margin-left: 220px}
183+
.toc-item .fa-fw:first-child {
184+
cursor: pointer;
185+
}

src/jupyter_contrib_nbextensions/nbextensions/toc2/main.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ define(["require", "jquery", "base/js/namespace", 'services/config',
3333
'navigate_text': '#333333',
3434
'navigate_num': '#000000',
3535
},
36-
skip_h1_title: false,
36+
collapse_to_match_collapsible_headings: false,
37+
skip_h1_title: false,
3738
}
3839

3940
//.....................global variables....
@@ -73,10 +74,11 @@ define(["require", "jquery", "base/js/namespace", 'services/config',
7374
try
7475
{cfg.colors = IPython.notebook.metadata.toc.colors = $.extend(true, cfg.colors, config.data.toc2.colors); }
7576
catch(e) {}
76-
// and moveMenuLeft, threshold, wideNotebook taken globally (if it exists, otherwise default)
77+
// and moveMenuLeft, threshold, wideNotebook, collapse_to_match_collapsible_headings taken globally (if it exists, otherwise default)
7778
cfg.moveMenuLeft = IPython.notebook.metadata.toc.moveMenuLeft = initial_cfg.moveMenuLeft;
7879
cfg.threshold = IPython.notebook.metadata.toc.threshold = initial_cfg.threshold;
7980
cfg.widenNotebook = IPython.notebook.metadata.toc.widenNotebook = initial_cfg.widenNotebook;
81+
cfg.collapse_to_match_collapsible_headings = IPython.notebook.metadata.toc.collapse_to_match_collapsible_headings = initial_cfg.collapse_to_match_collapsible_headings
8082
if (config.data.toc2) {
8183
if (typeof config.data.toc2.moveMenuLeft !== "undefined") {
8284
cfg.moveMenuLeft = IPython.notebook.metadata.toc.moveMenuLeft = config.data.toc2.moveMenuLeft;
@@ -87,6 +89,9 @@ define(["require", "jquery", "base/js/namespace", 'services/config',
8789
if (typeof config.data.toc2.widenNotebook !== "undefined") {
8890
cfg.widenNotebook = IPython.notebook.metadata.toc.widenNotebook = config.data.toc2.widenNotebook;
8991
}
92+
if (typeof config.data.toc2.collapse_to_match_collapsible_headings !== "undefined") {
93+
cfg.collapse_to_match_collapsible_headings = IPython.notebook.metadata.toc.collapse_to_match_collapsible_headings = config.data.toc2.collapse_to_match_collapsible_headings;
94+
}
9095
}
9196
// create highlights style section in document
9297
create_additional_css();
@@ -138,7 +143,7 @@ define(["require", "jquery", "base/js/namespace", 'services/config',
138143

139144
function create_additional_css() {
140145
var sheet = document.createElement('style')
141-
sheet.innerHTML = "#toc-level0 li > a:hover { display: block; background-color: " + cfg.colors.hover_highlight + " }\n" +
146+
sheet.innerHTML = "#toc-level0 li > span:hover { background-color: " + cfg.colors.hover_highlight + " }\n" +
142147
".toc-item-highlight-select {background-color: " + cfg.colors.selected_highlight + "}\n" +
143148
".toc-item-highlight-execute {background-color: " + cfg.colors.running_highlight + "}\n" +
144149
".toc-item-highlight-execute.toc-item-highlight-select {background-color: " + cfg.colors.selected_highlight + "}"
@@ -168,7 +173,7 @@ define(["require", "jquery", "base/js/namespace", 'services/config',
168173
callbacks.shell.reply = function(msg) {
169174
if (msg.msg_type === 'execute_reply') {
170175
setTimeout(function(){
171-
$(toc).find('.toc-item-highlight-execute').removeClass('toc-item-highlight-execute')
176+
$('.toc .toc-item-highlight-execute').removeClass('toc-item-highlight-execute');
172177
rehighlight_running_cells() // re-highlight running cells
173178
}, 100);
174179
var c = IPython.notebook.get_selected_cell();
@@ -210,6 +215,7 @@ define(["require", "jquery", "base/js/namespace", 'services/config',
210215
$([IPython.events]).on("kernel_ready.Kernel", function() {
211216
addSaveAsWithToc();
212217
})
218+
213219
// add a save as HTML with toc included
214220
addSaveAsWithToc();
215221
//

0 commit comments

Comments
 (0)