123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import {
- IKernel
- } from 'jupyter-js-services';
- import {
- Message
- } from 'phosphor/lib/core/messaging';
- import {
- Widget
- } from 'phosphor/lib/ui/widget';
- import {
- ABCWidgetFactory, IDocumentModel, IDocumentContext
- } from '../docregistry';
- import {
- HTML_COMMON_CLASS
- } from '../renderers/widget';
- import * as d3Dsv from 'd3-dsv';
- import {
- PanelLayout
- } from 'phosphor/lib/ui/panel';
- /**
- * The class name added to a csv widget.
- */
- const CSV_CLASS = 'jp-CSVWidget';
- /**
- * The class name added to a csv toolbar widget.
- */
- const CSV_TOOLBAR_CLASS = 'jp-CSVWidget-toolbar';
- /**
- * The class name added to a csv toolbar's dropdown element.
- */
- const CSV_TOOLBAR_DROPDOWN_CLASS = 'jp-CSVWidget-toolbarDropdown';
- /**
- * The class name added to a csv table widget.
- */
- const CSV_TABLE_CLASS = 'jp-CSVWidget-table';
- /**
- * The class name added to a csv warning widget.
- */
- const CSV_WARNING_CLASS = 'jp-CSVWidget-warning';
- /**
- * The hard limit on the number of rows to display.
- */
- const DISPLAY_LIMIT = 1000;
- /**
- * A widget for csv tables.
- */
- export
- class CSVWidget extends Widget {
- /**
- * Construct a new csv table widget.
- */
- constructor(context: IDocumentContext<IDocumentModel>) {
- super();
- this._context = context;
- this.node.tabIndex = -1;
- this.addClass(CSV_CLASS);
- this.layout = new PanelLayout();
- this._toolbar = new Widget({ node: createDelimiterSwitcherNode() });
- this._toolbar.addClass(CSV_TOOLBAR_CLASS);
- this._table = new Widget();
- this._table.addClass(CSV_TABLE_CLASS);
- this._table.addClass(HTML_COMMON_CLASS);
- this._warning = new Widget();
- this._warning.addClass(CSV_WARNING_CLASS);
- let layout = this.layout as PanelLayout;
- layout.addWidget(this._toolbar);
- layout.addWidget(this._table);
- layout.addWidget(this._warning);
- let select = this._toolbar.node.getElementsByClassName(
- CSV_TOOLBAR_DROPDOWN_CLASS)[0] as HTMLSelectElement;
- if (context.model.toString()) {
- this.update();
- }
- context.pathChanged.connect(() => {
- this.update();
- });
- context.model.contentChanged.connect(() => {
- this.update();
- });
- context.contentsModelChanged.connect(() => {
- this.update();
- });
- // Change delimiter on a change in the dropdown.
- select.addEventListener('change', event => {
- this.delimiter = select.value;
- this.update();
- });
- }
- /**
- * Dispose of the resources used by the widget.
- */
- dispose(): void {
- if (this.isDisposed) {
- return;
- }
- this._context = null;
- super.dispose();
- }
- /**
- * Handle `update-request` messages for the widget.
- */
- protected onUpdateRequest(msg: Message): void {
- this.title.label = this._context.path.split('/').pop();
- let cm = this._context.contentsModel;
- if (cm === null) {
- return;
- }
- let content = this._context.model.toString();
- let delimiter = this.delimiter as string;
- this.renderTable(content, delimiter);
- }
- /**
- * Render an html table from a csv string.
- */
- renderTable(content: string, delimiter: string) {
- let parsed = d3Dsv.dsvFormat(delimiter).parse(content);
- let table = document.createElement('table');
- let header = document.createElement('thead');
- let body = document.createElement('tbody');
- for (let name of parsed.columns) {
- let th = document.createElement('th');
- th.textContent = name;
- header.appendChild(th);
- }
- for (let row of parsed.slice(0, DISPLAY_LIMIT)) {
- let tr = document.createElement('tr');
- for (let col of parsed.columns) {
- let td = document.createElement('td');
- td.textContent = row[col];
- tr.appendChild(td);
- }
- body.appendChild(tr);
- }
- let msg = `Table is too long to render, rendering ${DISPLAY_LIMIT} of ` +
- `${parsed.length} rows`;
- if (parsed.length > DISPLAY_LIMIT) {
- this._warning.node.textContent = msg;
- } else {
- this._warning.node.textContent = '';
- }
- table.appendChild(header);
- table.appendChild(body);
- this._table.node.textContent = '';
- this._table.node.appendChild(table);
- }
- /**
- * Handle `'activate-request'` messages.
- */
- protected onActivateRequest(msg: Message): void {
- this.node.focus();
- }
- private _context: IDocumentContext<IDocumentModel>;
- private delimiter: string = ',';
- private _toolbar: Widget = null;
- private _table: Widget = null;
- private _warning: Widget = null;
- }
- /**
- * Create the node for the delimiter switcher.
- */
- function createDelimiterSwitcherNode(): HTMLElement {
- let div = document.createElement('div');
- let label = document.createElement('span');
- label.textContent = 'Delimiter:';
- let select = document.createElement('select');
- for (let delim of [',', ';', '\t']) {
- let option = document.createElement('option');
- option.value = delim;
- if (delim === '\t') {
- option.textContent = '\\t';
- } else {
- option.textContent = delim;
- }
- select.appendChild(option);
- }
- select.className = CSV_TOOLBAR_DROPDOWN_CLASS;
- div.appendChild(label);
- div.appendChild(select);
- return div;
- }
- /**
- * A widget factory for csv tables.
- */
- export
- class CSVWidgetFactory extends ABCWidgetFactory<CSVWidget, IDocumentModel> {
- /**
- * Create a new widget given a context.
- */
- createNew(context: IDocumentContext<IDocumentModel>, kernel?: IKernel.IModel): CSVWidget {
- let widget = new CSVWidget(context);
- this.widgetCreated.emit(widget);
- return widget;
- }
- }
|