Add Match Row and Delete Row commands
Match Row: selects all rows where the cell in the current column equals the current cell's value. Selection spans first-to-last matching row across all columns. Delete Row (Cmd+Backspace): deletes every row in the current selection, or the single current row if nothing is selected. Ensures at least one empty row always remains. Co-authored-by: Shelley <shelley@exe.dev>
This commit is contained in:
parent
ab2d281aad
commit
0e68de4278
|
|
@ -98,7 +98,7 @@ func TestParseCSVString(t *testing.T) {
|
|||
func TestGetCommands(t *testing.T) {
|
||||
reg := NewCommandRegistry()
|
||||
cmds := reg.GetCommands()
|
||||
if len(cmds) != 15 {
|
||||
if len(cmds) != 17 {
|
||||
t.Errorf("expected 12 commands, got %d", len(cmds))
|
||||
}
|
||||
// Check that all have IDs
|
||||
|
|
|
|||
|
|
@ -33,5 +33,7 @@ func (c *CommandRegistry) GetCommands() []Command {
|
|||
{ID: "sort-asc", Name: "Sort A-Z", Shortcut: ""},
|
||||
{ID: "sort-desc", Name: "Sort Z-A", Shortcut: ""},
|
||||
{ID: "sort-advanced", Name: "Sort Advanced", Shortcut: ""},
|
||||
{ID: "match-row", Name: "Match Row", Shortcut: ""},
|
||||
{ID: "delete-row", Name: "Delete Row", Shortcut: "Cmd+Backspace"},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -491,6 +491,13 @@ document.addEventListener('keydown', (e) => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Cmd+Backspace deletes rows
|
||||
if (meta && e.key === 'Backspace') {
|
||||
e.preventDefault();
|
||||
executeCommand('delete-row');
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete/Backspace clears selected cells
|
||||
if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||
e.preventDefault();
|
||||
|
|
@ -615,6 +622,8 @@ async function loadCommands() {
|
|||
{ ID: 'sort-asc', Name: 'Sort A-Z', Shortcut: '' },
|
||||
{ ID: 'sort-desc', Name: 'Sort Z-A', Shortcut: '' },
|
||||
{ ID: 'sort-advanced', Name: 'Sort Advanced', Shortcut: '' },
|
||||
{ ID: 'match-row', Name: 'Match Row', Shortcut: '' },
|
||||
{ ID: 'delete-row', Name: 'Delete Row', Shortcut: 'Cmd+Backspace' },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -720,6 +729,8 @@ async function executeCommand(id) {
|
|||
case 'sort-asc': doSort(true); break;
|
||||
case 'sort-desc': doSort(false); break;
|
||||
case 'sort-advanced': openSortAdvanced(); break;
|
||||
case 'match-row': doMatchRow(); break;
|
||||
case 'delete-row': doDeleteRow(); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -933,6 +944,59 @@ function doInsertColRight() {
|
|||
setStatus('Inserted column right');
|
||||
}
|
||||
|
||||
// ===== Match & Delete Row =====
|
||||
function doMatchRow() {
|
||||
const col = state.cursor.col;
|
||||
const val = getCellValue(state.cursor.row, col);
|
||||
const matchingRows = [];
|
||||
for (let r = 0; r < state.rows.length; r++) {
|
||||
if ((state.rows[r][col] || '') === val) {
|
||||
matchingRows.push(r);
|
||||
}
|
||||
}
|
||||
if (matchingRows.length === 0) {
|
||||
setStatus('No matching rows');
|
||||
return;
|
||||
}
|
||||
// Select from first to last matching row, spanning all columns
|
||||
const first = matchingRows[0];
|
||||
const last = matchingRows[matchingRows.length - 1];
|
||||
state.cursor = { row: first, col: 0 };
|
||||
state.selection = {
|
||||
startRow: first,
|
||||
startCol: 0,
|
||||
endRow: last,
|
||||
endCol: state.headers.length - 1,
|
||||
};
|
||||
updateSelectionClasses();
|
||||
scrollCursorIntoView();
|
||||
const colName = state.headers[col] || colLabel(col);
|
||||
setStatus(`${matchingRows.length} row(s) matching ${colName} = "${val}"`);
|
||||
}
|
||||
|
||||
function doDeleteRow() {
|
||||
const sel = normalizeSelection();
|
||||
let startRow, endRow;
|
||||
if (sel) {
|
||||
startRow = sel.r1;
|
||||
endRow = sel.r2;
|
||||
} else {
|
||||
startRow = state.cursor.row;
|
||||
endRow = state.cursor.row;
|
||||
}
|
||||
const count = endRow - startRow + 1;
|
||||
state.rows.splice(startRow, count);
|
||||
// Ensure at least one row remains
|
||||
if (state.rows.length === 0) {
|
||||
state.rows.push(new Array(state.headers.length).fill(''));
|
||||
}
|
||||
// Adjust cursor
|
||||
state.cursor.row = Math.min(startRow, state.rows.length - 1);
|
||||
state.selection = null;
|
||||
render();
|
||||
setStatus(`Deleted ${count} row(s)`);
|
||||
}
|
||||
|
||||
// ===== Sorting =====
|
||||
function doSort(ascending) {
|
||||
const col = state.cursor.col;
|
||||
|
|
|
|||
Loading…
Reference in a new issue