UNPKG

@jupyterlab/notebook

Version:
281 lines 11 kB
/* * Copyright (c) Jupyter Development Team. * Distributed under the terms of the Modified BSD License. */ import { DOMUtils } from '@jupyterlab/apputils'; import { showPopup, TextItem } from '@jupyterlab/statusbar'; import { nullTranslator } from '@jupyterlab/translation'; import { classes, lineFormIcon, ReactWidget, VDomModel, VDomRenderer } from '@jupyterlab/ui-components'; import React from 'react'; /** * A component for rendering a "go-to-cell" form. */ class CellNumberFormComponent extends React.Component { /** * Construct a new CellNumberFormComponent. */ constructor(props) { super(props); /** * Handle a change to the value in the input field. */ this._handleChange = (event) => { this.setState({ value: event.currentTarget.value }); }; /** * Handle submission of the input field. */ this._handleSubmit = (event) => { var _a, _b; event.preventDefault(); const value = parseInt((_b = (_a = this._textInput) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '', 10); if (!isNaN(value) && isFinite(value) && 1 <= value && value <= this.props.maxCell) { this.props.handleSubmit(value); } return false; }; /** * Handle focusing of the input field. */ this._handleFocus = () => { this.setState({ hasFocus: true }); }; /** * Handle blurring of the input field. */ this._handleBlur = () => { this.setState({ hasFocus: false }); }; this._textInput = null; const translator = props.translator || nullTranslator; this._trans = translator.load('jupyterlab'); this.state = { value: '', hasFocus: false, textInputId: DOMUtils.createDomID() + '-cell-number-input' }; } /** * Focus the element on mount. */ componentDidMount() { var _a; (_a = this._textInput) === null || _a === void 0 ? void 0 : _a.focus(); } /** * Render the CellNumberFormComponent. */ render() { return (React.createElement("div", { className: "jp-lineFormSearch" }, React.createElement("form", { name: "cellNumberForm", onSubmit: this._handleSubmit, noValidate: true }, React.createElement("div", { className: classes('jp-lineFormWrapper', 'lm-lineForm-wrapper', this.state.hasFocus ? 'jp-lineFormWrapperFocusWithin' : undefined) }, React.createElement("input", { type: "number", id: this.state.textInputId, className: "jp-lineFormInput", min: 1, max: this.props.maxCell, onChange: this._handleChange, onFocus: this._handleFocus, onBlur: this._handleBlur, value: this.state.value, ref: input => { this._textInput = input; } }), React.createElement("div", { className: "jp-baseLineForm jp-lineFormButtonContainer" }, React.createElement(lineFormIcon.react, { className: "jp-baseLineForm jp-lineFormButtonIcon", elementPosition: "center" }), React.createElement("input", { type: "submit", className: "jp-baseLineForm jp-lineFormButton", value: "" }))), React.createElement("label", { className: "jp-lineFormCaption", htmlFor: this.state.textInputId }, this._trans.__('Go to cell number between 1 and %1', this.props.maxCell))))); } } /** * A pure functional component for rendering a notebook cell counter. */ function CellCounterComponent(props) { const translator = props.translator || nullTranslator; const trans = translator.load('jupyterlab'); const source = props.selectionStart > 0 && props.selectionStart !== props.selectionEnd ? trans.__('%1:%2/%3', props.selectionStart, props.selectionEnd, props.totalCells) : trans.__('Cell %1/%2', props.activeCell, props.totalCells); const keydownHandler = (event) => { if (event.key === 'Enter' || event.key === 'Spacebar' || event.key === ' ') { event.preventDefault(); event.stopPropagation(); props.handleClick(); } }; return (React.createElement(TextItem, { role: "button", "aria-haspopup": true, onClick: props.handleClick, source: source, title: trans.__('Go to cell…'), tabIndex: 0, onKeyDown: keydownHandler })); } /** * A widget implementing a notebook cell counter status item. */ export class CellCounterStatus extends VDomRenderer { /** * Construct a new CellCounterStatus status item. */ constructor(options = {}) { super(new CellCounterStatus.Model()); this._popup = null; this.addClass('jp-mod-highlighted'); this._translator = options.translator || nullTranslator; } /** * Render the status item. */ render() { if (this.model === null) { return null; } return (React.createElement(CellCounterComponent, { activeCell: this.model.activeCell, selectionStart: this.model.selectionStart, selectionEnd: this.model.selectionEnd, totalCells: this.model.totalCells, translator: this._translator, handleClick: () => this._handleClick() })); } /** * A click handler for the widget. */ _handleClick() { if (this.model.totalCells < 1) { return; } if (this._popup) { this._popup.dispose(); } const body = ReactWidget.create(React.createElement(CellNumberFormComponent, { handleSubmit: value => this._handleSubmit(value), maxCell: this.model.totalCells, translator: this._translator })); this._popup = showPopup({ body, anchor: this, align: 'right' }); } /** * Handle submission for the widget. */ _handleSubmit(value) { var _a; const notebook = this.model.notebook; if (!notebook) { return; } const cellIndex = value - 1; notebook.activeCellIndex = cellIndex; notebook.deselectAll(); void notebook.scrollToItem(cellIndex).catch(reason => { console.error('Go to cell', reason); }); (_a = this._popup) === null || _a === void 0 ? void 0 : _a.dispose(); notebook.activate(); } } /** * A namespace for CellCounterStatus statics. */ (function (CellCounterStatus) { /** * A VDom model for a status item tracking active and total notebook cells. */ class Model extends VDomModel { constructor() { super(...arguments); this._activeCell = 0; this._selectionStart = 0; this._selectionEnd = 0; this._totalCells = 0; this._notebook = null; } /** * The notebook tracked by this model. */ get notebook() { return this._notebook; } set notebook(notebook) { const oldNotebook = this._notebook; if (oldNotebook) { oldNotebook.activeCellChanged.disconnect(this._onChanged, this); oldNotebook.modelContentChanged.disconnect(this._onChanged, this); oldNotebook.selectionChanged.disconnect(this._onChanged, this); } const oldState = this._getAllState(); this._notebook = notebook; if (!this._notebook) { this._activeCell = 0; this._selectionStart = 0; this._selectionEnd = 0; this._totalCells = 0; } else { this._notebook.activeCellChanged.connect(this._onChanged, this); this._notebook.modelContentChanged.connect(this._onChanged, this); this._notebook.selectionChanged.connect(this._onChanged, this); this._updateStateFromNotebook(this._notebook); } this._triggerChange(oldState, this._getAllState()); } /** * The current active cell index shown to users (1-based). */ get activeCell() { return this._activeCell; } /** * The first selected cell index shown to users (1-based). */ get selectionStart() { return this._selectionStart; } /** * The last selected cell index shown to users (1-based). */ get selectionEnd() { return this._selectionEnd; } /** * The total number of cells. */ get totalCells() { return this._totalCells; } /** * React to notebook changes by refreshing the tracked state. */ _onChanged(notebook) { const oldState = this._getAllState(); this._updateStateFromNotebook(notebook); this._triggerChange(oldState, this._getAllState()); } _updateStateFromNotebook(notebook) { const activeCellIndex = notebook.activeCellIndex; this._activeCell = activeCellIndex >= 0 ? activeCellIndex + 1 : 0; this._totalCells = notebook.widgets.length; let selectionStart = this._activeCell; let selectionEnd = this._activeCell; let seenSelection = false; notebook.widgets.forEach((cell, index) => { if (!notebook.isSelectedOrActive(cell)) { return; } const oneBasedIndex = index + 1; if (!seenSelection) { selectionStart = oneBasedIndex; selectionEnd = oneBasedIndex; seenSelection = true; return; } selectionEnd = oneBasedIndex; }); this._selectionStart = seenSelection ? selectionStart : 0; this._selectionEnd = seenSelection ? selectionEnd : 0; } _getAllState() { return { activeCell: this._activeCell, selectionStart: this._selectionStart, selectionEnd: this._selectionEnd, totalCells: this._totalCells }; } _triggerChange(oldState, newState) { if (oldState.activeCell !== newState.activeCell || oldState.selectionStart !== newState.selectionStart || oldState.selectionEnd !== newState.selectionEnd || oldState.totalCells !== newState.totalCells) { this.stateChanged.emit(void 0); } } } CellCounterStatus.Model = Model; })(CellCounterStatus || (CellCounterStatus = {})); //# sourceMappingURL=cellcounterstatus.js.map