Skip to content

Commit

Permalink
feat(notebooks): keyboard actions for the cells
Browse files Browse the repository at this point in the history
  • Loading branch information
bsahitya committed Jul 16, 2024
1 parent e45cb47 commit 42a3ed4
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 24 deletions.
17 changes: 13 additions & 4 deletions libs/components/src/notebook-cell/notebook-cell.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ cv-code-editor {
background-color: var(--cv-theme-primary);
border-radius: 2px;
cursor: move;
height: 100%;
margin-top: 2px;
visibility: hidden;
}

.selectionIndicatorWrapper {
min-width: 8px;

&:hover {
.selectionIndicator {
visibility: visible;
}
}
}

.timesExecuted {
Expand All @@ -42,6 +52,7 @@ cv-code-editor {
height: 100%;
justify-content: end;
line-height: var(--mdc-typography-body2-line-height);
width: 50px;
text-wrap: nowrap;

.executionCount {
Expand All @@ -66,7 +77,6 @@ cv-code-editor {
display: flex;
justify-content: space-between;

&:hover,
&.focused,
&.selected {
.selectionIndicator {
Expand All @@ -90,15 +100,14 @@ cv-code-editor {
display: flex;
gap: 0.75rem;
justify-content: end;
width: calc(100% - 2rem);
width: calc(100% - 1.5rem);
}

.cellOutputWrapper {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 92%;
min-width: 92%;
width: calc(100% - 58px);
}

.output {
Expand Down
5 changes: 4 additions & 1 deletion libs/components/src/notebook-cell/notebook-cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ export class CovalentNotebookCell extends LitElement {
};
return html`
<div class="${classMap(classes)}">
<span class="selectionIndicator" draggable="true"></span>
<div class="selectionIndicatorWrapper" draggable="true">
<div class="selectionIndicator"></div>
</div>
<div class="cellCodeWrapper">
<span class="timesExecuted" draggable="true"
>${this.renderExecutionCount()}</span
Expand Down
3 changes: 2 additions & 1 deletion libs/components/src/notebook/notebook.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
flex-direction: column;
gap: 0.5rem;
height: calc(100% - 106px);
overflow: auto;
overflow-y: auto;
overflow-x: hidden;
padding: 1rem;
width: 100%;
}
Expand Down
1 change: 0 additions & 1 deletion libs/components/src/notebook/notebook.stories.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

137 changes: 120 additions & 17 deletions libs/components/src/notebook/notebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,18 @@ declare global {
}

interface CellData {
cell_type: string;
code: string;
errors?: string[];
execution_count?: number | null;
index?: number;
inputs?: { password: boolean; prompt: string }[];
language: string;
loading?: boolean;
metadata: {
tags?: string[];
trusted?: boolean;
};
outputs?: {
data: {
[key: string]: string;
Expand All @@ -43,6 +49,7 @@ interface CellData {
}[];
selected?: boolean;
showEditor?: boolean;
source: string;
timesExecuted?: number;
}

Expand Down Expand Up @@ -90,20 +97,48 @@ export class CovalentNotebook extends LitElement {
`,
];

constructor() {
super();
this._handleKeydown = this._handleKeydown.bind(this);
}

// Add a cell to the notebook
addCell(cellData: CellData) {
addCell(cellType: string) {
const cellData = this.createNewCell(cellType);
this._clipboardCell = { ...cellData };
this.pasteCell();
this._clipboardCell = null;
}

connectedCallback(): void {
super.connectedCallback();
document.addEventListener('keydown', this._handleKeydown);
}

// Copy the selected cell
copyCell() {
if (this._selectedCellIndex !== null) {
this._clipboardCell = { ...this.cells[this._selectedCellIndex] };
}
}

createNewCell(cellType: string): CellData {
const language =
cellType === 'markdown' ? cellType : this.defaultLanguage || 'python';
return {
cell_type: cellType,
code: '',
execution_count: null,
language: language,
metadata: {
tags: [],
trusted: false,
},
outputs: [],
source: '',
};
}

// Cut the selected cell
cutCell() {
if (this._selectedCellIndex !== null) {
Expand All @@ -116,7 +151,12 @@ export class CovalentNotebook extends LitElement {
deleteCell() {
if (this._selectedCellIndex !== null) {
this.cells.splice(this._selectedCellIndex, 1);
this._selectedCellIndex = null;
const selectedCellIndex = Math.min(
this._selectedCellIndex + 1,
this.cells.length - 1
);
// Select and scroll to the next cell
this.scrollToSelectedCell(selectedCellIndex);
this.dispatchUpdatedCells();
}
}
Expand All @@ -126,6 +166,11 @@ export class CovalentNotebook extends LitElement {
this.cells.forEach((cell) => (cell.selected = false));
}

disconnectedCallback(): void {
super.disconnectedCallback();
document.removeEventListener('keydown', this._handleKeydown);
}

// Dispatch a custom cell event
dispatchCustomCellEvent(name: string, cell?: CellData) {
if (!cell && this._selectedCellIndex) {
Expand Down Expand Up @@ -166,7 +211,7 @@ export class CovalentNotebook extends LitElement {
const cell = this.cells[cellIndex];

const template = html`
<div class="dragImage" style="min-width: 300px;">
<div class="dragImage" style="width: 80%;">
<cv-notebook-cell
.code="${cell.code}"
.language="${cell.language}"
Expand All @@ -183,6 +228,37 @@ export class CovalentNotebook extends LitElement {
return container;
}

scrollToSelectedCell(selectedCellIndex: number) {
this.selectCell(selectedCellIndex);
const selectedCellElement = this.shadowRoot?.querySelector(
`#cell-${this._selectedCellIndex}`
) as HTMLElement;
const container = this.shadowRoot?.querySelector(
'#notebook-cells'
) as HTMLElement;

if (selectedCellElement && container) {
const containerRect = container.getBoundingClientRect();
const cellRect = selectedCellElement.getBoundingClientRect();

const isAbove = cellRect.top < containerRect.top;
const isBelow = cellRect.bottom > containerRect.bottom;

// Scroll to the top of the cell
if (isAbove) {
container.scrollTop =
selectedCellElement.offsetTop - container.offsetTop;
} else if (isBelow) {
// Scroll to the bottom of the cell
container.scrollTop =
selectedCellElement.offsetTop -
container.offsetTop -
container.clientHeight +
selectedCellElement.clientHeight;
}
}
}

// Dispatch an event when the cell type is changed
handleCellTypeChange(e: CustomEvent) {
const cellType = this.cellTypes[e.detail.index];
Expand Down Expand Up @@ -286,6 +362,35 @@ export class CovalentNotebook extends LitElement {
}
}

private _handleKeydown(event: KeyboardEvent) {
let selectedCellIndex = this.cells?.findIndex((cell) => cell.selected);
switch (event.key) {
case 'ArrowUp':
selectedCellIndex = Math.max(selectedCellIndex - 1, 0);
this.scrollToSelectedCell(selectedCellIndex);
break;
case 'ArrowDown':
selectedCellIndex = Math.min(
selectedCellIndex + 1,
this.cells.length - 1
);
this.scrollToSelectedCell(selectedCellIndex);
break;
case 'Enter':
if (event.shiftKey) {
this.runCell();
} else {
const selectedCellElement = this.shadowRoot?.querySelector(
`#cell-${selectedCellIndex}`
);
const codeEditor =
selectedCellElement?.shadowRoot?.querySelector('cv-code-editor');
codeEditor?.editor?.focus();
}
break;
}
}

// Shows the cell editor on clicking output for markdown cells
handleOutputCLick(index: number) {
const cell = this.cells[index];
Expand Down Expand Up @@ -393,7 +498,7 @@ export class CovalentNotebook extends LitElement {
)}
</cv-select>
</section>
<section class="notebookCells">
<section class="notebookCells" id="notebook-cells">
${this.cells.map(
(cell, index) =>
html`<cv-notebook-cell
Expand Down Expand Up @@ -454,20 +559,10 @@ export class CovalentNotebook extends LitElement {
)}
</section>
<section class="notebookActions">
<cv-button
outlined
@click=${() =>
this.addCell({
code: '',
language: this.defaultLanguage || 'python',
})}
>
<cv-button outlined @click=${() => this.addCell('code')}>
<cv-icon style="font-size: 20px;">add</cv-icon>Code cell
</cv-button>
<cv-button
outlined
@click=${() => this.addCell({ code: '', language: 'markdown' })}
>
<cv-button outlined @click=${() => this.addCell('markdown')}>
<cv-icon style="font-size: 20px;">add</cv-icon>Markdown
</cv-button>
</section>
Expand All @@ -483,6 +578,11 @@ export class CovalentNotebook extends LitElement {
if (this._selectedCellIndex !== null) {
const cell = this.cells[this._selectedCellIndex];
this.dispatchCustomCellEvent('run-cell', cell);

// Select the next cell after running the current cell
if (this._selectedCellIndex + 1 < this.cells.length) {
this.scrollToSelectedCell(this._selectedCellIndex + 1);
}
}
this.requestUpdate();
}
Expand All @@ -505,8 +605,11 @@ export class CovalentNotebook extends LitElement {

protected updated(_changedProperties: PropertyValues): void {
if (_changedProperties.has('cells')) {
this.cells.forEach((cell) => {
this.cells.forEach((cell, index) => {
cell.showEditor = this.shouldRenderEditor(cell);
if (cell.inputs?.length) {
this.scrollToSelectedCell(index);
}
});
}
}
Expand Down

0 comments on commit 42a3ed4

Please sign in to comment.