Skip to content

Commit

Permalink
chore(updated-cell-traversal): Updated cell traversal to move between…
Browse files Browse the repository at this point in the history
… notebook cells using up and down arrow (#381)

* chore(updated-cell-traversal): Updated cell traversal to move between notebook cells using up and down arrow

* fix(solve-text-component-issue): Fix traversal in text component
  • Loading branch information
priyakanabar-crest authored Dec 13, 2024
1 parent 8d74828 commit 9d77346
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 12 deletions.
103 changes: 102 additions & 1 deletion zt_frontend/src/components/CodeCellManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
@createCell="createCodeCell"
@copilotCompletion="copilotCompletion"
@updateTimers="updateTimers"
@navigateToCell="handleCellNavigation"
:ref="'cellComponent' + codeCell.id"
/>
<component
v-else
Expand All @@ -39,6 +41,8 @@
@componentValueChange="componentValueChange"
@deleteCell="deleteCell"
@createCell="createCodeCell"
@navigateToCell="handleCellNavigation"
:ref="'cellComponent' + codeCell.id"
/>
</v-container>
</template>
Expand Down Expand Up @@ -149,7 +153,104 @@ export default {
},
updateTimers(cellId: string, value: boolean) {
this.$emit("updateTimers", cellId, value);
},
},
scrollToCellView(cellId: string): void {
try {
const cell = document.getElementById(`codeCard${cellId}`);
if (!cell) {
return;
}
const viewport = {
top: window.scrollY,
bottom: window.scrollY + window.innerHeight
};
const cellPosition = {
top: cell.getBoundingClientRect().top + viewport.top,
bottom: cell.getBoundingClientRect().bottom + viewport.top
};
const isOutsideViewport =
cellPosition.top < viewport.top ||
cellPosition.bottom > viewport.bottom;
if (isOutsideViewport) {
cell.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
} catch (error) {
console.error('Failed to scroll to cell:', error);
}
},
handleCellNavigation(currentCellId: string, direction: 'up' | 'down') {
const cellIds = Object.keys(this.notebook.cells);
const currentIndex = cellIds.indexOf(currentCellId);
if (currentIndex === -1) {
return;
}
const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
if (targetIndex < 0 || targetIndex >= cellIds.length) {
// No cell to navigate to in the given direction
return;
}
const targetCellId = cellIds[targetIndex];
const targetCell = this.notebook.cells[targetCellId];
//scroll to the target cell
this.scrollToCellView(targetCellId);
this.$nextTick(async () => {
const cellComponentRef = this.$refs['cellComponent' + targetCellId];
if (!cellComponentRef) {
return;
}
const cellComponent = Array.isArray(cellComponentRef)
? cellComponentRef[0]
: cellComponentRef;
if (!cellComponent || typeof cellComponent.getEditorView !== 'function') {
return;
}
try {
const view = await cellComponent.getEditorView();
if (view) {
if (view.focus) {
view.focus();
}
if (targetCell.cellType === 'text') {
const contentLength = view.getContent({ format: 'text' }).length;
const pos = direction === 'up' ? contentLength : 0;
const body = view.getBody();
const lastNode = body.lastChild;
if (lastNode) {
const range = document.createRange();
view.selection.setRng(range);
}
}
else {
// Handle CodeMirror editor
const pos = direction === 'up' ? view.state.doc.length : 0;
if (view.dispatch) {
view.dispatch({
selection: { anchor: pos },
});
}
}
}
} catch (error) {
console.error('Error accessing editor view:', error);
}
});
},
},
};
</script>
Expand Down
26 changes: 25 additions & 1 deletion zt_frontend/src/components/CodeComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ export default {
"saveCell",
"copilotCompletion",
"updateTimers",
"navigateToCell",
],
data() {
return {
Expand Down Expand Up @@ -263,7 +264,27 @@ export default {
return false;
},
},
]);
{
key: 'ArrowUp',
run: (view) => {
if (view.state.selection.main.from === 0) {
this.$emit('navigateToCell', this.cellData.id, 'up');
return true;
}
return false;
},
},
{
key: 'ArrowDown',
run: (view) => {
if (view.state.selection.main.to === view.state.doc.length) {
this.$emit('navigateToCell', this.cellData.id, 'down');
return true;
}
return false;
},
},
]);
const fetchSuggestion = async (state: any) => {
if (globalState.copilot_active) {
Expand Down Expand Up @@ -528,6 +549,9 @@ export default {
this.view.dispatch({});
}
},
getEditorView() {
return this.view || null;
},
},
};
</script>
Expand Down
64 changes: 61 additions & 3 deletions zt_frontend/src/components/EditorComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<tiny-editor
v-if="$devMode && !isAppRoute && !isMobile"
v-model="cellData.code"
:init="init"
:init="editorConfig"
@keyUp="saveCell"
/>
</template>
Expand Down Expand Up @@ -55,7 +55,7 @@ export default {
},
},
inheritAttrs: false,
emits: ["saveCell", "deleteCell", "createCell"],
emits: ["saveCell", "deleteCell", "createCell", 'navigateToCell'],
data() {
return {
init: {
Expand Down Expand Up @@ -97,6 +97,7 @@ export default {
{ title: "Markdown" },
{ title: "Text" },
],
editor: null,
};
},
computed: {
Expand All @@ -107,8 +108,37 @@ export default {
isMobile() {
return this.$vuetify.display.mobile;
},
editorConfig() {
return {
...this.init,
setup: (editor: any) => {
// Store editor reference on setup
this.editor = editor;
editor.on('init', () => {
this.handleEditorInit(editor);
});
editor.on('keydown', (e: any) => {
if (e.keyCode === 38) { // Up arrow
const isAtStart = this.isCursorAtStart(editor);
if (isAtStart) {
e.preventDefault();
this.$emit('navigateToCell', this.cellData.id, 'up');
}
}
if (e.keyCode === 40) { // Down arrow
const isAtEnd = this.isCursorAtEnd(editor);
if (isAtEnd) {
e.preventDefault();
this.$emit('navigateToCell', this.cellData.id, 'down');
}
}
});
}
};
}
},
mounted() {},
methods: {
saveCell() {
if (!this.$devMode) return;
Expand All @@ -120,9 +150,37 @@ export default {
createCell(cellType: string) {
this.$emit("createCell", this.cellData.id, cellType);
},
handleEditorInit(editor: any) {
this.editor = editor;
},
isCursorAtStart(editor: any): boolean {
const selection = editor.selection;
const rng = selection.getRng(true);
const isAtStart = rng.startOffset === 0 && rng.startContainer === editor.getBody().firstChild;
return isAtStart;
},
isCursorAtEnd(editor: any): boolean {
const selection = editor.selection;
const rng = selection.getRng(true);
const lastNode = editor.getBody().lastChild;
let isAtEnd = false;
if (rng.endContainer.nodeType === Node.TEXT_NODE) {
isAtEnd = rng.startOffset === rng.endContainer.length;
} else {
isAtEnd = rng.endContainer === lastNode && rng.startOffset === 0;
}
const isCursorAtEnd = isAtEnd && rng.endContainer === lastNode;
return isCursorAtEnd;
},
getEditorView() {
return this.editor;
},
},
};
</script>

<style>
.tox .tox-toolbar,
.tox .tox-toolbar__primary,
Expand Down
50 changes: 46 additions & 4 deletions zt_frontend/src/components/MarkdownComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
:viewportMargin="Infinity"
:extensions="extensions"
@keyup="saveCell"
@ready="handleReady"
/>
</template>
<template v-slot:outcome>
Expand All @@ -30,7 +31,7 @@
</template>

<script lang="ts">
import type { PropType } from "vue";
import type { PropType, ShallowRef } from "vue";
import { marked } from 'marked';
import { Codemirror } from 'vue-codemirror'
import { markdown } from '@codemirror/lang-markdown'
Expand All @@ -40,19 +41,57 @@ import { CodeCell } from "@/types/notebook";
import AddCell from '@/components/AddCell.vue'
import { useRoute } from 'vue-router'
import Cell from '@/components/Cell.vue'
import {EditorView, keymap} from "@codemirror/view";
import {Prec} from "@codemirror/state";
export default {
components: {
"add-cell": AddCell,
"cell": Cell,
"codemirror": Codemirror,
},
setup() {
const view: ShallowRef<EditorView | null> = shallowRef(null);
const handleReady = (payload: any) => {
view.value = payload.view;
};
return { view, handleReady };
},
computed: {
hasCellContent() {
const hasOutput = Boolean(this.cellData.code?.trim());
return hasOutput
},
extensions() {return [markdown(), oneDark, autocompletion({ override: [] })]},
extensions() {
const keyMap = keymap.of([
{
key: 'ArrowUp',
run: (view) => {
if (view.state.selection.main.from === 0) {
this.$emit('navigateToCell', this.cellData.id, 'up');
return true;
}
return false;
},
},
{
key: 'ArrowDown',
run: (view) => {
if (view.state.selection.main.to === view.state.doc.length) {
this.$emit('navigateToCell', this.cellData.id, 'down');
return true;
}
return false;
},
}
]);
return [Prec.highest(keyMap),markdown(), oneDark, autocompletion({ override: [] })]
},
compiledMarkdown() {
const pasrsed_markdown = marked.parse(this.cellData.code,)
Expand All @@ -78,7 +117,7 @@ export default {
};
},
inheritAttrs: false,
emits: ['saveCell', 'deleteCell', 'createCell'],
emits: ['saveCell', 'deleteCell', 'createCell','navigateToCell'],
props: {
cellData: {
type: Object as PropType<CodeCell>,
Expand All @@ -95,7 +134,10 @@ export default {
},
createCell(cellType: string){
this.$emit("createCell", this.cellData.id, cellType);
}
},
getEditorView() {
return this.view || null;
},
},
};
</script>
Expand Down
Loading

0 comments on commit 9d77346

Please sign in to comment.