tableeditor.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*-----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import { ISettingRegistry } from '@jupyterlab/coreutils';
  6. import { Message } from '@phosphor/messaging';
  7. import { Widget } from '@phosphor/widgets';
  8. import * as React from 'react';
  9. import * as ReactDOM from 'react-dom';
  10. /**
  11. * A tabular editor for plugin settings.
  12. */
  13. export class TableEditor extends Widget {
  14. /**
  15. * Create a new table editor for settings.
  16. */
  17. constructor(options: TableEditor.IOptions) {
  18. super({ node: document.createElement('fieldset') });
  19. this.addClass('jp-SettingsTableEditor');
  20. }
  21. /**
  22. * Tests whether the settings have been modified and need saving.
  23. */
  24. get isDirty(): boolean {
  25. return false; // TODO: remove placeholder.
  26. }
  27. /**
  28. * The plugin settings.
  29. */
  30. get settings(): ISettingRegistry.ISettings | null {
  31. return this._settings;
  32. }
  33. set settings(settings: ISettingRegistry.ISettings | null) {
  34. if (this._settings) {
  35. this._settings.changed.disconnect(this._onSettingsChanged, this);
  36. }
  37. this._settings = settings;
  38. if (this._settings) {
  39. this._settings.changed.connect(
  40. this._onSettingsChanged,
  41. this
  42. );
  43. }
  44. this.update();
  45. }
  46. /**
  47. * Handle `'update-request'` messages.
  48. */
  49. protected onUpdateRequest(msg: Message): void {
  50. const settings = this._settings;
  51. // Populate if possible.
  52. if (settings) {
  53. Private.populateTable(this.node, settings);
  54. }
  55. }
  56. /**
  57. * Handle setting changes.
  58. */
  59. private _onSettingsChanged(): void {
  60. this.update();
  61. }
  62. private _settings: ISettingRegistry.ISettings | null = null;
  63. }
  64. /**
  65. * A namespace for `TableEditor` statics.
  66. */
  67. export namespace TableEditor {
  68. /**
  69. * The instantiation options for a table editor.
  70. */
  71. export interface IOptions {
  72. /**
  73. * A function the table editor calls on save errors.
  74. */
  75. onSaveError: (reason: any) => void;
  76. }
  77. }
  78. /**
  79. * A namespace for private module data.
  80. */
  81. namespace Private {
  82. /**
  83. * Populate the fieldset with a specific plugin's metadata.
  84. */
  85. export function populateTable(
  86. node: HTMLElement,
  87. settings: ISettingRegistry.ISettings
  88. ): void {
  89. const { plugin, schema } = settings;
  90. const fields: { [property: string]: React.ReactElement<any> } = {};
  91. const properties = schema.properties || {};
  92. const title = `(${plugin}) ${schema.description}`;
  93. const label = `Fields - ${schema.title || plugin}`;
  94. Object.keys(properties).forEach(property => {
  95. const field = properties[property];
  96. const { type } = field;
  97. const defaultValue = settings.default(property);
  98. const title = field.title || property;
  99. const value = JSON.stringify(defaultValue) || '';
  100. const valueTitle = JSON.stringify(defaultValue, null, 4);
  101. fields[property] = (
  102. <tr key={property}>
  103. <td className="jp-SettingsTableEditor-key" title={title}>
  104. <code title={title}>{property}</code>
  105. </td>
  106. <td className="jp-SettingsTableEditor-value" title={valueTitle}>
  107. <code title={valueTitle}>{value}</code>
  108. </td>
  109. <td className="jp-SettingsTableEditor-type">{type}</td>
  110. </tr>
  111. );
  112. });
  113. const rows = Object.keys(fields)
  114. .sort((a, b) => a.localeCompare(b))
  115. .map(property => fields[property]);
  116. const fragment = (
  117. <React.Fragment>
  118. <legend title={title}>{label}</legend>
  119. <div className="jp-SettingsTableEditor-wrapper">
  120. <table>
  121. <thead>
  122. <tr>
  123. <th className="jp-SettingsTableEditor-key">Key</th>
  124. <th className="jp-SettingsTableEditor-value">Default</th>
  125. <th className="jp-SettingsTableEditor-type">Type</th>
  126. </tr>
  127. </thead>
  128. <tbody>{rows}</tbody>
  129. </table>
  130. </div>
  131. </React.Fragment>
  132. );
  133. ReactDOM.unmountComponentAtNode(node);
  134. ReactDOM.render(fragment, node);
  135. }
  136. }