forked from jupyterlab/jupyterlab-toc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtoc.tsx
194 lines (181 loc) · 5 KB
/
toc.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { ActivityMonitor, PathExt } from '@jupyterlab/coreutils';
import { IDocumentManager } from '@jupyterlab/docmanager';
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { Message } from '@lumino/messaging';
import { Widget } from '@lumino/widgets';
import { IHeading } from './utils/headings';
import { TableOfContentsRegistry as Registry } from './registry';
import { TOCTree } from './toc_tree';
/**
* Timeout for throttling ToC rendering.
*
* @private
*/
const RENDER_TIMEOUT = 1000;
/**
* Widget for hosting a notebook table of contents.
*/
export class TableOfContents extends Widget {
/**
* Returns a new table of contents.
*
* @param options - options
* @returns widget
*/
constructor(options: TableOfContents.IOptions) {
super();
this._docmanager = options.docmanager;
this._rendermime = options.rendermime;
}
/**
* Current widget-generator tuple for the ToC.
*/
get current(): TableOfContents.ICurrentWidget | null {
return this._current;
}
set current(value: TableOfContents.ICurrentWidget | null) {
// If they are the same as previously, do nothing...
if (
value &&
this._current &&
this._current.widget === value.widget &&
this._current.generator === value.generator
) {
return;
}
this._current = value;
if (this.generator && this.generator.toolbarGenerator) {
this._toolbar = this.generator.toolbarGenerator();
}
// Dispose an old activity monitor if one existed...
if (this._monitor) {
this._monitor.dispose();
this._monitor = null;
}
// If we are wiping the ToC, update and return...
if (!this._current) {
this.update();
return;
}
// Find the document model associated with the widget:
const context = this._docmanager.contextForWidget(this._current.widget);
if (!context || !context.model) {
throw Error('Could not find a context for the Table of Contents');
}
// Throttle the rendering rate of the table of contents:
this._monitor = new ActivityMonitor({
signal: context.model.contentChanged,
timeout: RENDER_TIMEOUT
});
this._monitor.activityStopped.connect(this.update, this);
this.update();
}
/**
* Current table of contents generator.
*
* @returns table of contents generator
*/
get generator() {
if (this._current) {
return this._current.generator;
}
return null;
}
/**
* Callback invoked upon an update request.
*
* @param msg - message
*/
protected onUpdateRequest(msg: Message): void {
let toc: IHeading[] = [];
let title = 'Table of Contents';
if (this._current) {
toc = this._current.generator.generate(this._current.widget);
const context = this._docmanager.contextForWidget(this._current.widget);
if (context) {
title = PathExt.basename(context.localPath);
}
}
let itemRenderer: (item: IHeading) => JSX.Element | null = (
item: IHeading
) => {
return <span>{item.text}</span>;
};
if (this._current && this._current.generator.itemRenderer) {
itemRenderer = this._current.generator.itemRenderer!;
}
let jsx = (
<div className="jp-TableOfContents">
<header>{title}</header>
</div>
);
if (this._current && this._current.generator) {
jsx = (
<TOCTree
title={title}
toc={toc}
generator={this.generator}
itemRenderer={itemRenderer}
toolbar={this._toolbar}
/>
);
}
ReactDOM.render(jsx, this.node, () => {
if (
this._current &&
this._current.generator.usesLatex === true &&
this._rendermime.latexTypesetter
) {
this._rendermime.latexTypesetter.typeset(this.node);
}
});
}
/**
* Callback invoked to re-render after showing a table of contents.
*
* @param msg - message
*/
protected onAfterShow(msg: Message): void {
this.update();
}
private _toolbar: any;
private _rendermime: IRenderMimeRegistry;
private _docmanager: IDocumentManager;
private _current: TableOfContents.ICurrentWidget | null;
private _monitor: ActivityMonitor<any, any> | null;
}
/**
* A namespace for TableOfContents statics.
*/
export namespace TableOfContents {
/**
* Interface describing table of contents widget options.
*/
export interface IOptions {
/**
* Application document manager.
*/
docmanager: IDocumentManager;
/**
* Application rendered MIME type.
*/
rendermime: IRenderMimeRegistry;
}
/**
* Interface describing the current widget.
*/
export interface ICurrentWidget<W extends Widget = Widget> {
/**
* Current widget.
*/
widget: W;
/**
* Table of contents generator for the current widget.
*/
generator: Registry.IGenerator<W>;
}
}