index.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. IIterator, map, toArray
  5. } from 'phosphor/lib/algorithm/iteration';
  6. import {
  7. JSONObject
  8. } from 'phosphor/lib/algorithm/json';
  9. import {
  10. Vector
  11. } from 'phosphor/lib/collections/vector';
  12. import {
  13. DisposableDelegate, IDisposable
  14. } from 'phosphor/lib/core/disposable';
  15. import {
  16. Token
  17. } from 'phosphor/lib/core/token';
  18. import {
  19. h, VNode
  20. } from 'phosphor/lib/ui/vdom';
  21. import {
  22. ICommandLinker
  23. } from '../commandlinker';
  24. import {
  25. VDomModel, VDomWidget
  26. } from '../common/vdom';
  27. /**
  28. * The command IDs used by the launcher plugin.
  29. */
  30. export
  31. namespace CommandIDs {
  32. export
  33. const show = 'launcher-jupyterlab:show';
  34. };
  35. /* tslint:disable */
  36. /**
  37. * The launcher token.
  38. */
  39. export
  40. const ILauncher = new Token<ILauncher>('jupyter.services.launcher');
  41. /* tslint:enable */
  42. /**
  43. * The class name added to LauncherWidget instances.
  44. */
  45. const LAUNCHER_CLASS = 'jp-LauncherWidget';
  46. /**
  47. * The class name added to LauncherWidget image nodes.
  48. */
  49. const IMAGE_CLASS = 'jp-LauncherWidget-image';
  50. /**
  51. * The class name added to LauncherWidget text nodes.
  52. */
  53. const TEXT_CLASS = 'jp-LauncherWidget-text';
  54. /**
  55. * The class name added to LauncherWidget item nodes.
  56. */
  57. const ITEM_CLASS = 'jp-LauncherWidget-item';
  58. /**
  59. * The class name added to LauncherWidget folder node.
  60. */
  61. const FOLDER_CLASS = 'jp-LauncherWidget-folder';
  62. /**
  63. * The class name added for the folder icon from default-theme.
  64. */
  65. const FOLDER_ICON_CLASS = 'jp-FolderIcon';
  66. /**
  67. * The class name added to LauncherWidget path nodes.
  68. */
  69. const PATH_CLASS = 'jp-LauncherWidget-path';
  70. /**
  71. * The class name added to LauncherWidget current working directory node.
  72. */
  73. const CWD_CLASS = 'jp-LauncherWidget-cwd';
  74. /**
  75. * The class name added to LauncherWidget body nodes.
  76. */
  77. const BODY_CLASS = 'jp-LauncherWidget-body';
  78. /**
  79. * The class name added to LauncherWidget dialog node.
  80. */
  81. const DIALOG_CLASS = 'jp-LauncherWidget-dialog';
  82. /**
  83. * The launcher interface.
  84. */
  85. export
  86. interface ILauncher {
  87. /**
  88. * Add a command item to the launcher, and trigger re-render event for parent
  89. * widget.
  90. *
  91. * @param options - The specification options for a launcher item.
  92. *
  93. * @returns A disposable that will remove the item from Launcher, and trigger
  94. * re-render event for parent widget.
  95. *
  96. */
  97. add(options: ILauncherItem): IDisposable;
  98. }
  99. /**
  100. * The specification for a launcher item.
  101. */
  102. export
  103. interface ILauncherItem {
  104. /**
  105. * The display name of the launcher item.
  106. */
  107. name: string;
  108. /**
  109. * The ID of the command that is called to launch the item.
  110. */
  111. command: string;
  112. /**
  113. * The command arguments, if any, needed to launch the item.
  114. */
  115. args?: JSONObject;
  116. /**
  117. * The image class name to attach to the launcher item. Defaults to
  118. * 'jp-Image' followed by the `name` with spaces removed. So if the name is
  119. * 'Launch New Terminal' the class name will be 'jp-ImageLaunchNewTerminal'.
  120. */
  121. imgClassName?: string;
  122. }
  123. /**
  124. * LauncherModel keeps track of the path to working directory and has a list of
  125. * LauncherItems, which the LauncherWidget will render.
  126. */
  127. export
  128. class LauncherModel extends VDomModel implements ILauncher {
  129. /**
  130. * Create a new launcher model.
  131. */
  132. constructor() {
  133. super();
  134. this._items = new Vector<ILauncherItem>();
  135. }
  136. /**
  137. * The path to the current working directory.
  138. */
  139. get path(): string {
  140. return this._path;
  141. }
  142. set path(path: string) {
  143. if (path === this._path) {
  144. return;
  145. }
  146. this._path = path;
  147. this.stateChanged.emit(void 0);
  148. }
  149. /**
  150. * Add a command item to the launcher, and trigger re-render event for parent
  151. * widget.
  152. *
  153. * @param options - The specification options for a launcher item.
  154. *
  155. * @returns A disposable that will remove the item from Launcher, and trigger
  156. * re-render event for parent widget.
  157. *
  158. */
  159. add(options: ILauncherItem): IDisposable {
  160. // Create a copy of the options to circumvent mutations to the original.
  161. let item = JSON.parse(JSON.stringify(options));
  162. // If image class name is not set, use the default value.
  163. item.imgClassName = item.imgClassName ||
  164. `jp-Image${item.name.replace(/\ /g, '')}`;
  165. this._items.pushBack(item);
  166. this.stateChanged.emit(void 0);
  167. return new DisposableDelegate(() => {
  168. this._items.remove(item);
  169. this.stateChanged.emit(void 0);
  170. });
  171. }
  172. /**
  173. * Return an iterator of launcher items.
  174. */
  175. items(): IIterator<ILauncherItem> {
  176. return this._items.iter();
  177. }
  178. private _items: Vector<ILauncherItem> = null;
  179. private _path: string = 'home';
  180. }
  181. /**
  182. * A virtual-DOM-based widget for the Launcher.
  183. */
  184. export
  185. class LauncherWidget extends VDomWidget<LauncherModel> {
  186. /**
  187. * Construct a new launcher widget.
  188. */
  189. constructor(options: LauncherWidget.IOptions) {
  190. super();
  191. this.addClass(LAUNCHER_CLASS);
  192. this._linker = options.linker;
  193. }
  194. /**
  195. * Render the launcher to virtual DOM nodes.
  196. */
  197. protected render(): VNode | VNode[] {
  198. // Create an iterator that yields rendered item nodes.
  199. let children = map(this.model.items(), item => {
  200. let img = h.span({className: item.imgClassName + ' ' + IMAGE_CLASS});
  201. let text = h.span({className: TEXT_CLASS }, item.name);
  202. let attrs = this._linker.populateVNodeAttrs({
  203. className: ITEM_CLASS
  204. }, item.command, item.args);
  205. return h.div(attrs, [img, text]);
  206. });
  207. let folderImage = h.span({
  208. className: `${FOLDER_CLASS} ${FOLDER_ICON_CLASS}`
  209. });
  210. let p = this.model.path;
  211. let pathName = p.length ? `home > ${p.replace(/\//g, ' > ')}` : 'home';
  212. let path = h.span({ className: PATH_CLASS }, pathName );
  213. let cwd = h.div({ className: CWD_CLASS }, [folderImage, path]);
  214. let body = h.div({ className: BODY_CLASS }, toArray(children));
  215. return h.div({ className: DIALOG_CLASS }, [cwd, body]);
  216. }
  217. private _linker: ICommandLinker = null;
  218. }
  219. /**
  220. * A namespace for launcher widget statics.
  221. */
  222. export
  223. namespace LauncherWidget {
  224. /**
  225. * The instantiation option for a launcher widget.
  226. */
  227. export
  228. interface IOptions {
  229. /**
  230. * Command linker instance.
  231. */
  232. linker: ICommandLinker;
  233. }
  234. }