browser.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. Contents
  5. } from 'jupyter-js-services';
  6. import {
  7. Message
  8. } from 'phosphor/lib/core/messaging';
  9. import {
  10. CommandRegistry
  11. } from 'phosphor/lib/ui/commandregistry';
  12. import {
  13. Keymap
  14. } from 'phosphor/lib/ui/keymap';
  15. import {
  16. PanelLayout
  17. } from 'phosphor/lib/ui/panel';
  18. import {
  19. Widget
  20. } from 'phosphor/lib/ui/widget';
  21. import {
  22. DocumentManager
  23. } from '../docmanager';
  24. import {
  25. FileButtons
  26. } from './buttons';
  27. import {
  28. BreadCrumbs
  29. } from './crumbs';
  30. import {
  31. DirListing
  32. } from './listing';
  33. import {
  34. FileBrowserModel
  35. } from './model';
  36. import {
  37. FILE_BROWSER_CLASS, showErrorMessage
  38. } from './utils';
  39. /**
  40. * The class name added to the filebrowser crumbs node.
  41. */
  42. const CRUMBS_CLASS = 'jp-FileBrowser-crumbs';
  43. /**
  44. * The class name added to the filebrowser buttons node.
  45. */
  46. const BUTTON_CLASS = 'jp-FileBrowser-buttons';
  47. /**
  48. * The class name added to the filebrowser listing node.
  49. */
  50. const LISTING_CLASS = 'jp-FileBrowser-listing';
  51. /**
  52. * The duration of auto-refresh in ms.
  53. */
  54. const REFRESH_DURATION = 10000;
  55. /**
  56. * An interface for a widget opener.
  57. */
  58. export
  59. interface IWidgetOpener {
  60. open(widget: Widget): void;
  61. }
  62. /**
  63. * A widget which hosts a file browser.
  64. *
  65. * The widget uses the Jupyter Contents API to retreive contents,
  66. * and presents itself as a flat list of files and directories with
  67. * breadcrumbs.
  68. */
  69. export
  70. class FileBrowserWidget extends Widget {
  71. /**
  72. * Construct a new file browser.
  73. *
  74. * @param model - The file browser view model.
  75. */
  76. constructor(options: FileBrowserWidget.IOptions) {
  77. super();
  78. this.addClass(FILE_BROWSER_CLASS);
  79. let commands = this._commands = options.commands;
  80. let keymap = this._keymap = options.keymap;
  81. let manager = this._manager = options.manager;
  82. let model = this._model = options.model;
  83. let opener = this._opener = options.opener;
  84. let renderer = options.renderer;
  85. model.refreshed.connect(this._handleRefresh, this);
  86. this._crumbs = new BreadCrumbs({ model });
  87. this._buttons = new FileButtons({
  88. commands, keymap, manager, model, opener
  89. });
  90. this._listing = new DirListing({ manager, model, opener, renderer });
  91. model.fileChanged.connect((fbModel, args) => {
  92. if (args.newValue) {
  93. manager.handleRename(args.oldValue, args.newValue);
  94. } else {
  95. manager.handleDelete(args.oldValue);
  96. }
  97. });
  98. this._crumbs.addClass(CRUMBS_CLASS);
  99. this._buttons.addClass(BUTTON_CLASS);
  100. this._listing.addClass(LISTING_CLASS);
  101. let layout = new PanelLayout();
  102. layout.addWidget(this._buttons);
  103. layout.addWidget(this._crumbs);
  104. layout.addWidget(this._listing);
  105. this.layout = layout;
  106. }
  107. /**
  108. * Get the command registry used by the file browser.
  109. *
  110. * #### Notes
  111. * This is a read-only property.
  112. */
  113. get commands(): CommandRegistry {
  114. return this._commands;
  115. }
  116. /**
  117. * Get the keymap manager used by the file browser.
  118. *
  119. * #### Notes
  120. * This is a read-only property.
  121. */
  122. get keymap(): Keymap {
  123. return this._keymap;
  124. }
  125. /**
  126. * Get the model used by the file browser.
  127. *
  128. * #### Notes
  129. * This is a read-only property.
  130. */
  131. get model(): FileBrowserModel {
  132. return this._model;
  133. }
  134. /**
  135. * Dispose of the resources held by the file browser.
  136. */
  137. dispose() {
  138. this._model = null;
  139. this._crumbs = null;
  140. this._buttons = null;
  141. this._listing = null;
  142. this._manager = null;
  143. this._opener = null;
  144. super.dispose();
  145. }
  146. /**
  147. * Change directory.
  148. */
  149. cd(path: string): Promise<void> {
  150. return this._model.cd(path);
  151. }
  152. /**
  153. * Open the currently selected item(s).
  154. *
  155. * Changes to the first directory encountered.
  156. */
  157. open(): void {
  158. let foundDir = false;
  159. let items = this._model.items;
  160. for (let item of items) {
  161. if (!this._listing.isSelected(item.name)) {
  162. continue;
  163. }
  164. if (item.type === 'directory') {
  165. if (!foundDir) {
  166. foundDir = true;
  167. this._model.cd(item.name).catch(error =>
  168. showErrorMessage(this, 'Open directory', error)
  169. );
  170. }
  171. } else {
  172. this.openPath(item.path);
  173. }
  174. }
  175. }
  176. /**
  177. * Open a file by path.
  178. */
  179. openPath(path: string, widgetName='default'): Widget {
  180. return this._buttons.open(path, widgetName);
  181. }
  182. /**
  183. * Create a file from a creator.
  184. */
  185. createFrom(creatorName: string): Promise<Widget> {
  186. return this._buttons.createFrom(creatorName);
  187. }
  188. /**
  189. * Create a new untitled file in the current directory.
  190. */
  191. createNew(options: Contents.ICreateOptions): Promise<Widget> {
  192. let model = this.model;
  193. return model.newUntitled(options).then(contents => {
  194. return this._buttons.createNew(contents.path);
  195. });
  196. }
  197. /**
  198. * Rename the first currently selected item.
  199. */
  200. rename(): Promise<string> {
  201. return this._listing.rename();
  202. }
  203. /**
  204. * Cut the selected items.
  205. */
  206. cut(): void {
  207. this._listing.cut();
  208. }
  209. /**
  210. * Copy the selected items.
  211. */
  212. copy(): void {
  213. this._listing.copy();
  214. }
  215. /**
  216. * Paste the items from the clipboard.
  217. */
  218. paste(): Promise<void> {
  219. return this._listing.paste();
  220. }
  221. /**
  222. * Delete the currently selected item(s).
  223. */
  224. delete(): Promise<void> {
  225. return this._listing.delete();
  226. }
  227. /**
  228. * Duplicate the currently selected item(s).
  229. */
  230. duplicate(): Promise<void> {
  231. return this._listing.duplicate();
  232. }
  233. /**
  234. * Download the currently selected item(s).
  235. */
  236. download(): void {
  237. this._listing.download();
  238. }
  239. /**
  240. * Shut down kernels on the applicable currently selected items.
  241. */
  242. shutdownKernels(): Promise<void> {
  243. return this._listing.shutdownKernels();
  244. }
  245. /**
  246. * Refresh the current directory.
  247. */
  248. refresh(): Promise<void> {
  249. return this._model.refresh().catch(error => {
  250. showErrorMessage(this, 'Server Connection Error', error);
  251. });
  252. }
  253. /**
  254. * Select next item.
  255. */
  256. selectNext(): void {
  257. this._listing.selectNext();
  258. }
  259. /**
  260. * Select previous item.
  261. */
  262. selectPrevious(): void {
  263. this._listing.selectPrevious();
  264. }
  265. /**
  266. * Find a path given a click.
  267. */
  268. pathForClick(event: MouseEvent): string {
  269. return this._listing.pathForClick(event);
  270. }
  271. /**
  272. * A message handler invoked on an `'after-attach'` message.
  273. */
  274. protected onAfterAttach(msg: Message): void {
  275. super.onAfterAttach(msg);
  276. this.refresh();
  277. }
  278. /**
  279. * A message handler invoked on an `'after-show'` message.
  280. */
  281. protected onAfterShow(msg: Message): void {
  282. super.onAfterShow(msg);
  283. this.refresh();
  284. }
  285. /**
  286. * Handle a model refresh.
  287. */
  288. private _handleRefresh(): void {
  289. clearTimeout(this._timeoutId);
  290. this._timeoutId = setTimeout(() => this.refresh(), REFRESH_DURATION);
  291. }
  292. private _buttons: FileButtons = null;
  293. private _commands: CommandRegistry = null;
  294. private _crumbs: BreadCrumbs = null;
  295. private _keymap: Keymap = null;
  296. private _listing: DirListing = null;
  297. private _manager: DocumentManager = null;
  298. private _model: FileBrowserModel = null;
  299. private _opener: IWidgetOpener = null;
  300. private _timeoutId = -1;
  301. }
  302. /**
  303. * The namespace for the `FileBrowserWidget` class statics.
  304. */
  305. export
  306. namespace FileBrowserWidget {
  307. /**
  308. * An options object for initializing a file browser widget.
  309. */
  310. export
  311. interface IOptions {
  312. /**
  313. * The command registry for use with the file browser.
  314. */
  315. commands: CommandRegistry;
  316. /**
  317. * The keymap for use with the file browser.
  318. */
  319. keymap: Keymap;
  320. /**
  321. * A file browser model instance.
  322. */
  323. model: FileBrowserModel;
  324. /**
  325. * A document manager instance.
  326. */
  327. manager: DocumentManager;
  328. /**
  329. * A widget opener function.
  330. */
  331. opener: IWidgetOpener;
  332. /**
  333. * An optional renderer for the directory listing area.
  334. *
  335. * The default is a shared instance of `DirListing.Renderer`.
  336. */
  337. renderer?: DirListing.IRenderer;
  338. }
  339. }