index.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. JSONObject, PromiseDelegate
  5. } from '@phosphor/coreutils';
  6. import {
  7. Message,
  8. } from '@phosphor/messaging';
  9. import {
  10. Widget
  11. } from '@phosphor/widgets';
  12. import {
  13. IRenderMime
  14. } from '@jupyterlab/rendermime-interfaces';
  15. /**
  16. * Import vega-embed in this manner due to how it is exported.
  17. */
  18. import embed = require('vega-embed');
  19. /**
  20. * The CSS class to add to the Vega and Vega-Lite widget.
  21. */
  22. const VEGA_COMMON_CLASS = 'jp-RenderedVegaCommon';
  23. /**
  24. * The CSS class to add to the Vega.
  25. */
  26. const VEGA_CLASS = 'jp-RenderedVega';
  27. /**
  28. * The CSS class to add to the Vega-Lite.
  29. */
  30. const VEGALITE_CLASS = 'jp-RenderedVegaLite';
  31. /**
  32. * The MIME type for Vega.
  33. *
  34. * #### Notes
  35. * The version of this follows the major version of Vega.
  36. */
  37. export
  38. const VEGA_MIME_TYPE = 'application/vnd.vega.v2+json';
  39. /**
  40. * The MIME type for Vega-Lite.
  41. *
  42. * #### Notes
  43. * The version of this follows the major version of Vega-Lite.
  44. */
  45. export
  46. const VEGALITE_MIME_TYPE = 'application/vnd.vegalite.v1+json';
  47. /**
  48. * A widget for rendering Vega or Vega-Lite data, for usage with rendermime.
  49. */
  50. export
  51. class RenderedVega extends Widget implements IRenderMime.IReadyWidget {
  52. /**
  53. * Create a new widget for rendering Vega/Vega-Lite.
  54. */
  55. constructor(options: IRenderMime.IRenderOptions) {
  56. super();
  57. this.addClass(VEGA_COMMON_CLASS);
  58. this._model = options.model;
  59. // Handle things related to the MIME type.
  60. let mimeType = this._mimeType = options.mimeType;
  61. if (mimeType === VEGA_MIME_TYPE) {
  62. this.addClass(VEGA_CLASS);
  63. this._mode = 'vega';
  64. } else {
  65. this.addClass(VEGALITE_CLASS);
  66. this._mode = 'vega-lite';
  67. }
  68. }
  69. /**
  70. * A promise that resolves when the widget is ready.
  71. */
  72. get ready(): Promise<void> {
  73. return this._ready.promise;
  74. }
  75. /**
  76. * Dispose of the widget.
  77. */
  78. dispose(): void {
  79. this._model = null;
  80. super.dispose();
  81. }
  82. /**
  83. * Trigger rendering after the widget is attached to the DOM.
  84. */
  85. onAfterAttach(msg: Message): void {
  86. this._renderVega();
  87. }
  88. /**
  89. * Actual render Vega/Vega-Lite into this widget's node.
  90. */
  91. private _renderVega(): void {
  92. let data = this._model.data.get(this._mimeType) as JSONObject;
  93. let embedSpec = {
  94. mode: this._mode,
  95. spec: data
  96. };
  97. embed(this.node, embedSpec, (error: any, result: any): any => {
  98. this._ready.resolve(undefined);
  99. // This is copied out for now as there is a bug in JupyterLab
  100. // that triggers and infinite rendering loop when this is done.
  101. // let imageData = result.view.toImageURL();
  102. // imageData = imageData.split(',')[1];
  103. // this._injector('image/png', imageData);
  104. });
  105. }
  106. private _model: IRenderMime.IMimeModel = null;
  107. private _mimeType: string;
  108. private _mode: string;
  109. private _ready = new PromiseDelegate<void>();
  110. }
  111. /**
  112. * A mime renderer for Vega/Vega-Lite data.
  113. */
  114. export
  115. class VegaRenderer implements IRenderMime.IRenderer {
  116. /**
  117. * The mimeTypes this renderer accepts.
  118. */
  119. mimeTypes = [VEGA_MIME_TYPE, VEGALITE_MIME_TYPE];
  120. /**
  121. * Whether the renderer can render given the render options.
  122. */
  123. canRender(options: IRenderMime.IRenderOptions): boolean {
  124. return this.mimeTypes.indexOf(options.mimeType) !== -1;
  125. }
  126. /**
  127. * Render the transformed mime bundle.
  128. */
  129. render(options: IRenderMime.IRenderOptions): IRenderMime.IReadyWidget {
  130. return new RenderedVega(options);
  131. }
  132. /**
  133. * Whether the renderer will sanitize the data given the render options.
  134. */
  135. wouldSanitize(options: IRenderMime.IRenderOptions): boolean {
  136. return false;
  137. }
  138. }
  139. const renderer = new VegaRenderer();
  140. const extensions: IRenderMime.IExtension | IRenderMime.IExtension[] = [
  141. // Vega
  142. {
  143. mimeType: VEGA_MIME_TYPE,
  144. renderer,
  145. rendererIndex: 0,
  146. dataType: 'json',
  147. widgetFactoryOptions: {
  148. name: 'Vega',
  149. fileExtensions: ['.vg', '.vg.json', 'json'],
  150. defaultFor: ['.vg', '.vg.json'],
  151. readOnly: true
  152. }
  153. },
  154. // Vega-Lite
  155. {
  156. mimeType: VEGALITE_MIME_TYPE,
  157. renderer,
  158. rendererIndex: 0,
  159. dataType: 'json',
  160. widgetFactoryOptions: {
  161. name: 'Vega-Lite',
  162. fileExtensions: ['.vl', '.vl.json', 'json'],
  163. defaultFor: ['.vl', '.vl.json'],
  164. readOnly: true
  165. }
  166. }
  167. ];
  168. export default extensions;