Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature for moving the current block up and down #204

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/editor/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export const LANGUAGE_CHANGE = "heynote-change"
export const CURRENCIES_LOADED = "heynote-currencies-loaded"
export const SET_CONTENT = "heynote-set-content"
export const ADD_NEW_BLOCK = "heynote-add-new-block"
export const MOVE_BLOCK = "heynote-move-block"
58 changes: 57 additions & 1 deletion src/editor/block/commands.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EditorSelection } from "@codemirror/state"
import { heynoteEvent, LANGUAGE_CHANGE, CURRENCIES_LOADED, ADD_NEW_BLOCK } from "../annotation.js";
import { heynoteEvent, LANGUAGE_CHANGE, CURRENCIES_LOADED, ADD_NEW_BLOCK, MOVE_BLOCK } from "../annotation.js";
import { blockState, getActiveNoteBlock, getFirstNoteBlock, getLastNoteBlock, getNoteBlockFromPos } from "./block"
import { moveLineDown, moveLineUp } from "./move-lines.js";
import { selectAll } from "./select-all.js";
Expand Down Expand Up @@ -313,3 +313,59 @@ export function triggerCurrenciesLoaded(state, dispatch) {
annotations: [heynoteEvent.of(CURRENCIES_LOADED)],
}))
}

export function moveCurrentBlockUp({state, dispatch}) {
return moveCurrentBlock(state, dispatch, true)
}

export function moveCurrentBlockDown({state, dispatch}) {
return moveCurrentBlock(state, dispatch, false)
}

function moveCurrentBlock(state, dispatch, up) {
if (state.readOnly) {
return false
}

const blocks = state.facet(blockState)
const currentBlock = getActiveNoteBlock(state)
const blockIndex = blocks.indexOf(currentBlock)
if ((up && blockIndex === 0) || (!up && blockIndex === blocks.length - 1)) {
return false
}

const dir = up ? -1 : 1
const neighborBlock = blocks[blockIndex + dir]

const currentBlockContent = state.sliceDoc(currentBlock.delimiter.from, currentBlock.content.to)
const neighborBlockContent = state.sliceDoc(neighborBlock.delimiter.from, neighborBlock.content.to)
const newContent = up ? currentBlockContent + neighborBlockContent : neighborBlockContent + currentBlockContent

const selectionRange = state.selection.asSingle().ranges[0]
let newSelectionRange
if (up) {
newSelectionRange = EditorSelection.range(
selectionRange.anchor - currentBlock.delimiter.from + neighborBlock.delimiter.from,
selectionRange.head - currentBlock.delimiter.from + neighborBlock.delimiter.from,
)
} else {
newSelectionRange = EditorSelection.range(
selectionRange.anchor + neighborBlock.content.to - neighborBlock.delimiter.from,
selectionRange.head + neighborBlock.content.to - neighborBlock.delimiter.from,
)
}

dispatch(state.update({
changes: {
from: up ? neighborBlock.delimiter.from : currentBlock.delimiter.from,
to: up ? currentBlock.content.to : neighborBlock.content.to,
insert: newContent,
},
selection: newSelectionRange,
annotations: [heynoteEvent.of(MOVE_BLOCK)],
}, {
scrollIntoView: true,
userEvent: "input",
}))
return true
}
3 changes: 3 additions & 0 deletions src/editor/keymap.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
gotoPreviousParagraph, gotoNextParagraph,
selectNextParagraph, selectPreviousParagraph,
newCursorBelow, newCursorAbove,
moveCurrentBlockUp, moveCurrentBlockDown,
} from "./block/commands.js"
import { pasteCommand, copyCommand, cutCommand } from "./copy-paste.js"

Expand Down Expand Up @@ -65,5 +66,7 @@ export function heynoteKeymap(editor) {
{key:"Mod-ArrowDown", run:gotoNextBlock, shift:selectNextBlock},
{key:"Ctrl-ArrowUp", run:gotoPreviousParagraph, shift:selectPreviousParagraph},
{key:"Ctrl-ArrowDown", run:gotoNextParagraph, shift:selectNextParagraph},
["Mod-Shift-Alt-ArrowUp", moveCurrentBlockUp],
["Mod-Shift-Alt-ArrowDown", moveCurrentBlockDown],
])
}
112 changes: 112 additions & 0 deletions tests/move-block.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { expect, test } from "@playwright/test"
import { HeynotePage } from "./test-utils.js"

let heynotePage

test.beforeEach(async ({ page }) => {
heynotePage = new HeynotePage(page)
await heynotePage.goto()

expect((await heynotePage.getBlocks()).length).toBe(1)
await heynotePage.setContent(`
∞∞∞text
Block A
∞∞∞text
Block B
∞∞∞text
Block C`)

// check that blocks are created
expect((await heynotePage.getBlocks()).length).toBe(3)

// check that visual block layers are created
await expect(page.locator("css=.heynote-blocks-layer > div")).toHaveCount(3)
})

test("move the first block up", async ({ page }) => {
// select the first block, cursor position: "Block A|"
await page.locator("body").press("ArrowUp")
await page.locator("body").press("ArrowUp")

await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+Alt+ArrowUp`)
const cursorPosition = await heynotePage.getCursorPosition()
const content = await heynotePage.getContent()

expect((await heynotePage.getBlocks()).length).toBe(3)
expect(await heynotePage.getBlockContent(0)).toBe("Block A")
expect(await heynotePage.getBlockContent(1)).toBe("Block B")
expect(await heynotePage.getBlockContent(2)).toBe("Block C")
expect(content.slice(cursorPosition - 1, cursorPosition)).toBe("A")
})

test("move the middle block up", async ({ page }) => {
// select the second block, cursor position: "Block B|"
await page.locator("body").press("ArrowUp")

await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+Alt+ArrowUp`)
const cursorPosition = await heynotePage.getCursorPosition()
const content = await heynotePage.getContent()

expect((await heynotePage.getBlocks()).length).toBe(3)
expect(await heynotePage.getBlockContent(0)).toBe("Block B")
expect(await heynotePage.getBlockContent(1)).toBe("Block A")
expect(await heynotePage.getBlockContent(2)).toBe("Block C")
expect(content.slice(cursorPosition - 1, cursorPosition)).toBe("B")
})

test("move the last block up", async ({ page }) => {
// cursor position: "Block C|"
await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+Alt+ArrowUp`)
const cursorPosition = await heynotePage.getCursorPosition()
const content = await heynotePage.getContent()

expect((await heynotePage.getBlocks()).length).toBe(3)
expect(await heynotePage.getBlockContent(0)).toBe("Block A")
expect(await heynotePage.getBlockContent(1)).toBe("Block C")
expect(await heynotePage.getBlockContent(2)).toBe("Block B")
expect(content.slice(cursorPosition - 1, cursorPosition)).toBe("C")
})

test("move the first block down", async ({ page }) => {
// select the first block, cursor position: "Block A|"
await page.locator("body").press("ArrowUp")
await page.locator("body").press("ArrowUp")

await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+Alt+ArrowDown`)
const cursorPosition = await heynotePage.getCursorPosition()
const content = await heynotePage.getContent()

expect((await heynotePage.getBlocks()).length).toBe(3)
expect(await heynotePage.getBlockContent(0)).toBe("Block B")
expect(await heynotePage.getBlockContent(1)).toBe("Block A")
expect(await heynotePage.getBlockContent(2)).toBe("Block C")
expect(content.slice(cursorPosition - 1, cursorPosition)).toBe("A")
})

test("move the middle block down", async ({ page }) => {
// select the second block, cursor position: "Block B|"
await page.locator("body").press("ArrowUp")

await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+Alt+ArrowDown`)
const cursorPosition = await heynotePage.getCursorPosition()
const content = await heynotePage.getContent()

expect((await heynotePage.getBlocks()).length).toBe(3)
expect(await heynotePage.getBlockContent(0)).toBe("Block A")
expect(await heynotePage.getBlockContent(1)).toBe("Block C")
expect(await heynotePage.getBlockContent(2)).toBe("Block B")
expect(content.slice(cursorPosition - 1, cursorPosition)).toBe("B")
})

test("move the last block down", async ({ page }) => {
// cursor position: "Block C|"
await page.locator("body").press(`${heynotePage.isMac ? "Meta" : "Control"}+Shift+Alt+ArrowDown`)
const cursorPosition = await heynotePage.getCursorPosition()
const content = await heynotePage.getContent()

expect((await heynotePage.getBlocks()).length).toBe(3)
expect(await heynotePage.getBlockContent(0)).toBe("Block A")
expect(await heynotePage.getBlockContent(1)).toBe("Block B")
expect(await heynotePage.getBlockContent(2)).toBe("Block C")
expect(content.slice(cursorPosition - 1, cursorPosition)).toBe("C")
})
Loading