browser.ts 8.4 KB

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