inputarea.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /* -----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import { PanelLayout } from '@lumino/widgets';
  6. import { Widget } from '@lumino/widgets';
  7. import { CodeEditor, CodeEditorWrapper } from '@jupyterlab/codeeditor';
  8. import { CodeMirrorEditorFactory } from '@jupyterlab/codemirror';
  9. import { ICellModel } from './model';
  10. /**
  11. * The class name added to input area widgets.
  12. */
  13. const INPUT_AREA_CLASS = 'jp-InputArea';
  14. /**
  15. * The class name added to the prompt area of cell.
  16. */
  17. const INPUT_AREA_PROMPT_CLASS = 'jp-InputArea-prompt';
  18. /**
  19. * The class name added to OutputPrompt.
  20. */
  21. const INPUT_PROMPT_CLASS = 'jp-InputPrompt';
  22. /**
  23. * The class name added to the editor area of the cell.
  24. */
  25. const INPUT_AREA_EDITOR_CLASS = 'jp-InputArea-editor';
  26. /** ****************************************************************************
  27. * InputArea
  28. ******************************************************************************/
  29. /**
  30. * An input area widget, which hosts a prompt and an editor widget.
  31. */
  32. export class InputArea extends Widget {
  33. /**
  34. * Construct an input area widget.
  35. */
  36. constructor(options: InputArea.IOptions) {
  37. super();
  38. this.addClass(INPUT_AREA_CLASS);
  39. const model = (this.model = options.model);
  40. const contentFactory = (this.contentFactory =
  41. options.contentFactory || InputArea.defaultContentFactory);
  42. // Prompt
  43. const prompt = (this._prompt = contentFactory.createInputPrompt());
  44. prompt.addClass(INPUT_AREA_PROMPT_CLASS);
  45. // Editor
  46. const editorOptions = {
  47. model,
  48. factory: contentFactory.editorFactory,
  49. updateOnShow: options.updateOnShow
  50. };
  51. const editor = (this._editor = new CodeEditorWrapper(editorOptions));
  52. editor.addClass(INPUT_AREA_EDITOR_CLASS);
  53. const layout = (this.layout = new PanelLayout());
  54. layout.addWidget(prompt);
  55. layout.addWidget(editor);
  56. }
  57. /**
  58. * The model used by the widget.
  59. */
  60. readonly model: ICellModel;
  61. /**
  62. * The content factory used by the widget.
  63. */
  64. readonly contentFactory: InputArea.IContentFactory;
  65. /**
  66. * Get the CodeEditorWrapper used by the cell.
  67. */
  68. get editorWidget(): CodeEditorWrapper {
  69. return this._editor;
  70. }
  71. /**
  72. * Get the CodeEditor used by the cell.
  73. */
  74. get editor(): CodeEditor.IEditor {
  75. return this._editor.editor;
  76. }
  77. /**
  78. * Get the prompt node used by the cell.
  79. */
  80. get promptNode(): HTMLElement {
  81. return this._prompt.node;
  82. }
  83. /**
  84. * Render an input instead of the text editor.
  85. */
  86. renderInput(widget: Widget): void {
  87. const layout = this.layout as PanelLayout;
  88. if (this._rendered) {
  89. this._rendered.parent = null;
  90. }
  91. this._editor.hide();
  92. this._rendered = widget;
  93. layout.addWidget(widget);
  94. }
  95. /**
  96. * Show the text editor.
  97. */
  98. showEditor(): void {
  99. if (this._rendered) {
  100. this._rendered.parent = null;
  101. }
  102. this._editor.show();
  103. }
  104. /**
  105. * Set the prompt of the input area.
  106. */
  107. setPrompt(value: string): void {
  108. this._prompt.executionCount = value;
  109. }
  110. /**
  111. * Dispose of the resources held by the widget.
  112. */
  113. dispose() {
  114. // Do nothing if already disposed.
  115. if (this.isDisposed) {
  116. return;
  117. }
  118. this._prompt = null!;
  119. this._editor = null!;
  120. this._rendered = null!;
  121. super.dispose();
  122. }
  123. private _prompt: IInputPrompt;
  124. private _editor: CodeEditorWrapper;
  125. private _rendered: Widget;
  126. }
  127. /**
  128. * A namespace for `InputArea` statics.
  129. */
  130. export namespace InputArea {
  131. /**
  132. * The options used to create an `InputArea`.
  133. */
  134. export interface IOptions {
  135. /**
  136. * The model used by the widget.
  137. */
  138. model: ICellModel;
  139. /**
  140. * The content factory used by the widget to create children.
  141. *
  142. * Defaults to one that uses CodeMirror.
  143. */
  144. contentFactory?: IContentFactory;
  145. /**
  146. * Whether to send an update request to the editor when it is shown.
  147. */
  148. updateOnShow?: boolean;
  149. }
  150. /**
  151. * An input area widget content factory.
  152. *
  153. * The content factory is used to create children in a way
  154. * that can be customized.
  155. */
  156. export interface IContentFactory {
  157. /**
  158. * The editor factory we need to include in `CodeEditorWratter.IOptions`.
  159. *
  160. * This is a separate readonly attribute rather than a factory method as we need
  161. * to pass it around.
  162. */
  163. readonly editorFactory: CodeEditor.Factory;
  164. /**
  165. * Create an input prompt.
  166. */
  167. createInputPrompt(): IInputPrompt;
  168. }
  169. /**
  170. * Default implementation of `IContentFactory`.
  171. *
  172. * This defaults to using an `editorFactory` based on CodeMirror.
  173. */
  174. export class ContentFactory implements IContentFactory {
  175. /**
  176. * Construct a `ContentFactory`.
  177. */
  178. constructor(options: ContentFactory.IOptions = {}) {
  179. this._editor = options.editorFactory || defaultEditorFactory;
  180. }
  181. /**
  182. * Return the `CodeEditor.Factory` being used.
  183. */
  184. get editorFactory(): CodeEditor.Factory {
  185. return this._editor;
  186. }
  187. /**
  188. * Create an input prompt.
  189. */
  190. createInputPrompt(): IInputPrompt {
  191. return new InputPrompt();
  192. }
  193. private _editor: CodeEditor.Factory;
  194. }
  195. /**
  196. * A namespace for the input area content factory.
  197. */
  198. export namespace ContentFactory {
  199. /**
  200. * Options for the content factory.
  201. */
  202. export interface IOptions {
  203. /**
  204. * The editor factory used by the content factory.
  205. *
  206. * If this is not passed, a default CodeMirror editor factory
  207. * will be used.
  208. */
  209. editorFactory?: CodeEditor.Factory;
  210. }
  211. }
  212. /**
  213. * A function to create the default CodeMirror editor factory.
  214. */
  215. function _createDefaultEditorFactory(): CodeEditor.Factory {
  216. const editorServices = new CodeMirrorEditorFactory();
  217. return editorServices.newInlineEditor;
  218. }
  219. /**
  220. * The default editor factory singleton based on CodeMirror.
  221. */
  222. export const defaultEditorFactory: CodeEditor.Factory = _createDefaultEditorFactory();
  223. /**
  224. * The default `ContentFactory` instance.
  225. */
  226. export const defaultContentFactory = new ContentFactory({});
  227. }
  228. /** ****************************************************************************
  229. * InputPrompt
  230. ******************************************************************************/
  231. /**
  232. * The interface for the input prompt.
  233. */
  234. export interface IInputPrompt extends Widget {
  235. /**
  236. * The execution count of the prompt.
  237. */
  238. executionCount: string | null;
  239. }
  240. /**
  241. * The default input prompt implementation.
  242. */
  243. export class InputPrompt extends Widget implements IInputPrompt {
  244. /*
  245. * Create an output prompt widget.
  246. */
  247. constructor() {
  248. super();
  249. this.addClass(INPUT_PROMPT_CLASS);
  250. }
  251. /**
  252. * The execution count for the prompt.
  253. */
  254. get executionCount(): string | null {
  255. return this._executionCount;
  256. }
  257. set executionCount(value: string | null) {
  258. this._executionCount = value;
  259. if (value === null) {
  260. this.node.textContent = ' ';
  261. } else {
  262. this.node.textContent = `[${value || ' '}]:`;
  263. }
  264. }
  265. private _executionCount: string | null = null;
  266. }