Skip to content

Commit

Permalink
feat: adding more functionalities to markdown Cell (datalayer#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcos Alves committed Jun 28, 2024
1 parent 3b452ca commit e1702bc
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 69 deletions.
67 changes: 51 additions & 16 deletions packages/react/src/components/cell/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { useState, useEffect } from 'react';
import { CodeCell } from '@jupyterlab/cells';
import { CodeCell, MarkdownCell } from '@jupyterlab/cells';
import { KernelMessage } from '@jupyterlab/services';
import { Box } from '@primer/react';
import CellAdapter from './CellAdapter';
Expand All @@ -27,30 +27,52 @@ export type ICellProps = {
* Whether to execute directly the code cell or not.
*/
autoStart?: boolean;
/**
* Whether to show the toolbar for cell or not
*/
showToolbar?: boolean;
};

export const Cell = (props: ICellProps) => {
const { type='code', source = '', autoStart } = props;
const { type='code', source = '', autoStart, showToolbar=true } = props;
const { serverSettings, defaultKernel } = useJupyter();
const cellStore = useCellStore();
const [adapter, setAdapter] = useState<CellAdapter>();

const handleCodeCellState = (adapter: CellAdapter) => {
(adapter.codeCell as CodeCell).outputArea.outputLengthChanged?.connect(
(outputArea, outputsCount) => {
cellStore.setOutputsCount(outputsCount);
const handleCellInitEvents = (adapter: CellAdapter) => {
adapter.cell.model.contentChanged.connect(
(cellModel, changedArgs) => {
cellStore.setSource(cellModel.sharedModel.getSource());
}
);

if (adapter.cell instanceof CodeCell) {
adapter.cell.outputArea.outputLengthChanged?.connect(
(outputArea, outputsCount) => {
cellStore.setOutputsCount(outputsCount);
}
);
}

adapter.sessionContext.initialize().then(() => {
if (autoStart) {
if (!autoStart) {
return
}

// Perform auto-start for code or markdown cells
if (adapter.cell instanceof CodeCell) {
const execute = CodeCell.execute(
(adapter.codeCell as CodeCell),
adapter.cell,
adapter.sessionContext
);
execute.then((msg: void | KernelMessage.IExecuteReplyMsg) => {
cellStore.setKernelAvailable(true);
});
}

if (adapter.cell instanceof MarkdownCell) {
adapter.cell.rendered = true;
}
});
}

Expand All @@ -62,19 +84,32 @@ export const Cell = (props: ICellProps) => {
source,
serverSettings,
kernel: defaultKernel,
boxOptions: {showToolbar}
});
cellStore.setAdapter(adapter);
cellStore.setSource(source);
adapter.codeCell.model.contentChanged.connect(
(cellModel, changedArgs) => {
cellStore.setSource(cellModel.sharedModel.getSource());
handleCellInitEvents(adapter);
setAdapter(adapter);

const handleDblClick = (event: Event) => {
let target = event.target as HTMLElement;
/**
* Find the DOM searching by the markdown output class (since child elements can be clicked also)
* If a rendered markdown was found, then back cell to editor mode
*/
while (target && !target.classList.contains('jp-MarkdownOutput')) {
target = target.parentElement as HTMLElement;
}
);
if (target && target.classList.contains('jp-MarkdownOutput')) {
(adapter.cell as MarkdownCell).rendered = false;
}
};

if (type === 'code') {
handleCodeCellState(adapter);
}
setAdapter(adapter);
// Adds the event for double click and the removal on component's destroy
document.addEventListener('dblclick', handleDblClick);
return () => {
document.removeEventListener('dblclick', handleDblClick);
};
});
}
}, [source, defaultKernel, serverSettings]);
Expand Down
99 changes: 49 additions & 50 deletions packages/react/src/components/cell/CellAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,29 @@ import Kernel from '../../jupyter/kernel/Kernel';
import getMarked from '../notebook/marked/marked';
import CellCommands from './CellCommands';

interface BoxOptions {
showToolbar?: boolean;
}
export class CellAdapter {
private _codeCell: CodeCell | MarkdownCell | RawCell;
private _cell: CodeCell | MarkdownCell | RawCell;
private _kernel: Kernel;
private _panel: BoxPanel;
private _sessionContext: SessionContext;
private _type: 'code' | 'markdown' | 'raw'

constructor(options: CellAdapter.ICellAdapterOptions) {
const { type, source, serverSettings, kernel } = options;
const { type, source, serverSettings, kernel, boxOptions } = options;
this._kernel = kernel;
this._type = type;
this.setupCell(type, source, serverSettings, kernel);
this.setupCell(type, source, serverSettings, kernel, boxOptions);
}

private setupCell(
type = 'code',
source: string,
serverSettings: ServerConnection.ISettings,
kernel: Kernel
kernel: Kernel,
boxOptions?: BoxOptions
) {
const kernelManager =
kernel.kernelManager ??
Expand Down Expand Up @@ -210,41 +214,31 @@ export class CellAdapter {
languages,
});

const cellModel = createStandaloneCell({
cell_type: type,
source: source,
metadata: {},
});
const contentFactory = new Cell.ContentFactory({
editorFactory: factoryService.newInlineEditor.bind(factoryService),
});
if (type === 'code') {
this._codeCell = new CodeCell({
this._cell = new CodeCell({
rendermime,
model: new CodeCellModel({
sharedModel: createStandaloneCell({
cell_type: 'code',
source: source,
metadata: {},
}) as YCodeCell,
}),
contentFactory: new Cell.ContentFactory({
editorFactory: factoryService.newInlineEditor.bind(factoryService),
}),
model: new CodeCellModel({sharedModel: cellModel as YCodeCell}),
contentFactory: contentFactory,
});
} else if (type === 'markdown') {
this._codeCell = new MarkdownCell({
this._cell = new MarkdownCell({
rendermime,
model: new MarkdownCellModel({
sharedModel: createStandaloneCell({
cell_type: 'markdown',
source: source,
metadata: {},
}) as YMarkdownCell,
}),
contentFactory: new Cell.ContentFactory({
editorFactory: factoryService.newInlineEditor.bind(factoryService),
}),
model: new MarkdownCellModel({sharedModel: cellModel as YMarkdownCell}),
contentFactory: contentFactory,
});
}

this._codeCell.addClass('dla-Jupyter-Cell');
this._codeCell.initializeState();

this._cell.addClass('dla-Jupyter-Cell');
this._cell.initializeState();
if (this._type === 'markdown') {
(this._codeCell as MarkdownCell).rendered = false;
(this._cell as MarkdownCell).rendered = false;
}

this._sessionContext.kernelChanged.connect(
Expand All @@ -270,18 +264,18 @@ export class CellAdapter {
if (this._type === 'code') {
const lang = info.language_info;
const mimeType = mimeService.getMimeTypeByLanguage(lang);
this._codeCell.model.mimeType = mimeType;
this._cell.model.mimeType = mimeType;
}
});
});
const editor = this._codeCell.editor;
const editor = this._cell.editor;
const model = new CompleterModel();
const completer = new Completer({ editor, model });
const timeout = 1000;
const provider = new KernelCompleterProvider();
const reconciliator = new ProviderReconciliator({
context: {
widget: this._codeCell,
widget: this._cell,
editor,
session: this._sessionContext.session,
},
Expand All @@ -293,7 +287,7 @@ export class CellAdapter {
const provider = new KernelCompleterProvider();
handler.reconciliator = new ProviderReconciliator({
context: {
widget: this._codeCell,
widget: this._cell,
editor,
session: this._sessionContext.session,
},
Expand All @@ -303,9 +297,7 @@ export class CellAdapter {
});
handler.editor = editor;

if (this._type === 'code') {
CellCommands(commands, (this._codeCell as CodeCell)!, this._sessionContext, handler);
}
CellCommands(commands, this._cell!, this._sessionContext, handler);
completer.hide();
completer.addClass('jp-Completer-Cell');
Widget.attach(completer, document.body);
Expand All @@ -315,9 +307,9 @@ export class CellAdapter {
icon: runIcon,
onClick: () => {
if (this._type === 'code') {
CodeCell.execute(this._codeCell as CodeCell, this._sessionContext);
CodeCell.execute(this._cell as CodeCell, this._sessionContext);
} else if (this._type === 'markdown') {
(this.codeCell as MarkdownCell).rendered = true;
(this._cell as MarkdownCell).rendered = true;
}
},
tooltip: 'Run',
Expand All @@ -338,17 +330,23 @@ export class CellAdapter {
);

if (this._type === 'code') {
(this._codeCell as CodeCell).outputsScrolled = false;
(this._cell as CodeCell).outputsScrolled = false;
}
this._codeCell.activate();
this._cell.activate();

this._panel = new BoxPanel();
this._panel.direction = 'top-to-bottom';
this._panel.spacing = 0;
this._panel.addWidget(toolbar);
this._panel.addWidget(this._codeCell);
BoxPanel.setStretch(toolbar, 0);
BoxPanel.setStretch(this._codeCell, 1);

if (boxOptions?.showToolbar !== false) {
this._panel.addWidget(toolbar);
}
this._panel.addWidget(this._cell);

if (boxOptions?.showToolbar !== false) {
BoxPanel.setStretch(toolbar, 0);
}
BoxPanel.setStretch(this._cell, 1);
window.addEventListener('resize', () => {
this._panel.update();
});
Expand All @@ -359,8 +357,8 @@ export class CellAdapter {
return this._panel;
}

get codeCell(): CodeCell | MarkdownCell | RawCell {
return this._codeCell;
get cell(): CodeCell | MarkdownCell | RawCell {
return this._cell;
}

get sessionContext(): SessionContext {
Expand All @@ -373,9 +371,9 @@ export class CellAdapter {

execute = () => {
if (this._type === 'code') {
CodeCell.execute((this._codeCell as CodeCell), this._sessionContext);
CodeCell.execute((this._cell as CodeCell), this._sessionContext);
} else if (this._type === 'markdown') {
(this._codeCell as MarkdownCell).rendered = true;
(this._cell as MarkdownCell).rendered = true;
}
};
}
Expand All @@ -386,6 +384,7 @@ export namespace CellAdapter {
source: string;
serverSettings: ServerConnection.ISettings;
kernel: Kernel;
boxOptions?: BoxOptions;
};
}

Expand Down
12 changes: 9 additions & 3 deletions packages/react/src/components/cell/CellCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { CommandRegistry } from '@lumino/commands';
import { CompletionHandler } from '@jupyterlab/completer';
import { CodeCell } from '@jupyterlab/cells';
import { CodeCell, MarkdownCell, RawCell } from '@jupyterlab/cells';
import { SessionContext } from '@jupyterlab/apputils';

const cmdIds = {
Expand All @@ -16,7 +16,7 @@ const cmdIds = {

export const CellCommands = (
commandRegistry: CommandRegistry,
codeCell: CodeCell,
cell: CodeCell | MarkdownCell | RawCell,
sessionContext: SessionContext,
completerHandler: CompletionHandler
): void => {
Expand All @@ -29,7 +29,13 @@ export const CellCommands = (
execute: () => completerHandler.completer.selectActive(),
});
commandRegistry.addCommand('run:cell', {
execute: () => CodeCell.execute(codeCell, sessionContext),
execute: () => {
if (cell instanceof CodeCell) {
CodeCell.execute(cell, sessionContext)
} else if (cell instanceof MarkdownCell) {
(cell as MarkdownCell).rendered = true;
}
},
});
commandRegistry.addKeyBinding({
selector: '.jp-InputArea-editor.jp-mod-completer-enabled',
Expand Down

0 comments on commit e1702bc

Please sign in to comment.