vdom.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. IDisposable
  5. } from '@phosphor/disposable';
  6. import {
  7. Message
  8. } from '@phosphor/messaging';
  9. import {
  10. ISignal, Signal
  11. } from '@phosphor/signaling';
  12. import {
  13. VirtualDOM, VirtualNode
  14. } from '@phosphor/virtualdom';
  15. import {
  16. Widget
  17. } from '@phosphor/widgets';
  18. /**
  19. * Phosphor widget that encodes best practices for VDOM based rendering.
  20. */
  21. export
  22. abstract class VDomWidget<T extends VDomWidget.IModel> extends Widget {
  23. /**
  24. * A signal emited when the model changes.
  25. */
  26. get modelChanged(): ISignal<this, void> {
  27. return this._modelChanged;
  28. }
  29. /**
  30. * Set the model and fire changed signals.
  31. */
  32. set model(newValue: T | null) {
  33. newValue = newValue || null;
  34. if (this._model === newValue) {
  35. return;
  36. }
  37. if (this._model) {
  38. this._model.stateChanged.disconnect(this.update, this);
  39. }
  40. this._model = newValue;
  41. if (this._model) {
  42. this._model.stateChanged.connect(this.update, this);
  43. }
  44. this.update();
  45. this._modelChanged.emit(void 0);
  46. }
  47. /**
  48. * Get the current model.
  49. */
  50. get model(): T | null {
  51. return this._model;
  52. }
  53. /**
  54. * Dispose this widget.
  55. */
  56. dispose() {
  57. this._model = null;
  58. super.dispose();
  59. }
  60. /**
  61. * Called to update the state of the widget.
  62. *
  63. * The default implementation of this method triggers
  64. * VDOM based rendering by calling the this.render() method.
  65. */
  66. protected onUpdateRequest(msg: Message): void {
  67. let vnode = this.render();
  68. VirtualDOM.render(vnode, this.node);
  69. }
  70. /**
  71. * Render the content of this widget using the virtial DOM.
  72. *
  73. * This method will be called anytime the widget needs to be rendered,
  74. * which includes layout triggered rendering and all model changes.
  75. *
  76. * Subclasses should define this method and use the current model state
  77. * to create a virtual node or nodes to render.
  78. */
  79. protected abstract render(): VirtualNode | ReadonlyArray<VirtualNode>;
  80. private _model: T | null;
  81. private _modelChanged = new Signal<this, void>(this);
  82. }
  83. /**
  84. * The namespace for VDomWidget statics.
  85. */
  86. export
  87. namespace VDomWidget {
  88. /**
  89. * An interface for a model to be used with vdom rendering.
  90. */
  91. export
  92. interface IModel extends IDisposable {
  93. /**
  94. * A signal emited when any model state changes.
  95. */
  96. readonly stateChanged: ISignal<this, void>;
  97. }
  98. }
  99. /**
  100. * Concrete implementation of VDomWidget model.
  101. */
  102. export
  103. class VDomModel implements VDomWidget.IModel {
  104. /**
  105. * A signal emitted when any model state changes.
  106. */
  107. readonly stateChanged = new Signal<this, void>(this);
  108. /**
  109. * Test whether the model is disposed.
  110. */
  111. get isDisposed(): boolean {
  112. return this._isDisposed;
  113. }
  114. /**
  115. * Dispose the model.
  116. */
  117. dispose(): void {
  118. if (this.isDisposed) {
  119. return;
  120. }
  121. this._isDisposed = true;
  122. Signal.clearData(this);
  123. }
  124. private _isDisposed = false;
  125. }