Skip to content

Commit bb041fa

Browse files
committed
feat(commands): add delete row/column keybindings
Register two new command-palette entries with editable default keybindings, mirroring the existing insert/move command structure: - Markdown table: delete row — default Ctrl+Backspace - Markdown table: delete column — default Ctrl+Delete Both operate on the focused cell, guard against deleting the header row or the last remaining row/column, and stash a pendingToolbar entry on the neighbouring cell so the pinned toolbar and caret follow after re-render.
1 parent cd7af77 commit bb041fa

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

src/index.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import '@logseq/libs'
33
import parseMarkdownTable from './utils/parseRawInputByMarkdownIt'
44
import { splitStrByTable } from './utils/splitStrByTable'
55
import { looksLikeMarkdownTable, markdownTableToMatrix } from './utils/detectMarkdownTable'
6-
import { attachInlineEditing, insertInFocusedTableCell, moveCaretInFocusedTableCell, moveInFocusedTableCell, prepareInlineRenderer, resumePinnedToolbar } from './utils/inlineEditable'
6+
import { attachInlineEditing, deleteInFocusedTableCell, insertInFocusedTableCell, moveCaretInFocusedTableCell, moveInFocusedTableCell, prepareInlineRenderer, resumePinnedToolbar } from './utils/inlineEditable'
77
import i18n from './locales/i18n'
88
import './index.css'
99

@@ -170,6 +170,20 @@ if (isInBrowser) {
170170
keybinding: { binding: 'ctrl+alt+shift+left' }
171171
}, () => insertInFocusedTableCell('colLeft'))
172172

173+
// Row/column delete. No local keydown handler covers these — the
174+
// bindings below are the only way to trigger them via keyboard
175+
// (besides the right-click menu / pinned toolbar).
176+
logseq.App.registerCommandPalette({
177+
key: 'mdtable-delete-row',
178+
label: i18n.t('Markdown table: delete row'),
179+
keybinding: { binding: 'ctrl+backspace' }
180+
}, () => deleteInFocusedTableCell('row'))
181+
logseq.App.registerCommandPalette({
182+
key: 'mdtable-delete-col',
183+
label: i18n.t('Markdown table: delete column'),
184+
keybinding: { binding: 'ctrl+delete' }
185+
}, () => deleteInFocusedTableCell('col'))
186+
173187
// Row/column move. Defaults match the local Alt+Shift+Arrow handler
174188
// in `attachInlineEditing`.
175189
logseq.App.registerCommandPalette({

src/utils/inlineEditable.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,40 @@ export const moveInFocusedTableCell = (which) => {
257257
doMoveOp(root, opts, cell, which)
258258
}
259259

260+
// Delete the focused cell's row/column. `which` ∈ 'row' | 'col'. No-op
261+
// if focus isn't in a cell, if the row is the header (we need it to keep
262+
// the table valid markdown), or if the table only has one row/column left.
263+
export const deleteInFocusedTableCell = (which) => {
264+
let hostDoc = null
265+
try { hostDoc = (window.top || window.parent)?.document } catch (_) { /* cross-origin */ }
266+
if (!hostDoc) return
267+
const active = hostDoc.activeElement
268+
const cell = active && active.closest && active.closest('table.lsp-mdt th, table.lsp-mdt td')
269+
if (!cell) return
270+
const root = cell.closest('.lsp-mdtable-renderer')
271+
const opts = root && root._lspInlineOpts
272+
if (!opts) return
273+
const tableEl = cell.closest('table.lsp-mdt')
274+
const tr = cell.closest('tr')
275+
const rows = Array.from(tableEl.querySelectorAll('tr'))
276+
const rowIdx = rows.indexOf(tr)
277+
const cells = Array.from(tr.querySelectorAll('th,td'))
278+
const colIdx = cells.indexOf(cell)
279+
const ord = Array.from(root.querySelectorAll('table.lsp-mdt')).indexOf(tableEl)
280+
let op, newRow = rowIdx, newCol = colIdx
281+
if (which === 'row') {
282+
if (rowIdx < 1 || rows.length < 2) return // protect header / last row
283+
op = tableOps.deleteRow
284+
if (newRow >= rows.length - 1) newRow = rows.length - 2
285+
} else if (which === 'col') {
286+
if (cells.length < 2) return
287+
op = tableOps.deleteCol
288+
if (newCol >= cells.length - 1) newCol = cells.length - 2
289+
} else return
290+
pendingToolbar.set(opts.blockId, { ord, rowIdx: newRow, colIdx: newCol })
291+
commitStructural(root, opts, (m, i) => (i === ord ? op(m, rowIdx, colIdx) : m))
292+
}
293+
260294
// Shared by the local Alt+Shift+Arrow handler and the registered move
261295
// commands. Stashes a `pendingToolbar` entry at the moved cell's new
262296
// position so `resumePinnedToolbar` follows the cell after re-render.

0 commit comments

Comments
 (0)