UPDATE: Add keyboard shortcuts for move column/row
Also clean up a bit of the keyboard shortcuts mess
This commit is contained in:
committed by
Nicolás Hatcher Andrés
parent
caf26194df
commit
fb7886ca9e
@@ -607,7 +607,7 @@ impl Model {
|
||||
.set_cell_style(target_row, c, style_idx)?;
|
||||
}
|
||||
|
||||
let worksheet = &mut self.workbook.worksheets[sheet as usize];
|
||||
let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
|
||||
let mut new_rows = Vec::new();
|
||||
for r in worksheet.rows.iter() {
|
||||
if r.r == row {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
constants::LAST_ROW,
|
||||
constants::{LAST_COLUMN, LAST_ROW},
|
||||
expressions::utils::{is_valid_column_number, is_valid_row},
|
||||
worksheet::NavigationDirection,
|
||||
};
|
||||
@@ -114,7 +114,7 @@ impl UserModel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the selected range. Note that the selected cell must be in one of the corners.
|
||||
/// Sets the selected range. Note that the selected cell must be in the selected range.
|
||||
pub fn set_selected_range(
|
||||
&mut self,
|
||||
start_row: i32,
|
||||
@@ -148,16 +148,32 @@ impl UserModel {
|
||||
if let Some(view) = worksheet.views.get_mut(&0) {
|
||||
let selected_row = view.row;
|
||||
let selected_column = view.column;
|
||||
// The selected cells must be on one of the corners of the selected range:
|
||||
if selected_row != start_row && selected_row != end_row {
|
||||
return Err(format!(
|
||||
"The selected cells is not in one of the corners. Row: '{selected_row}' and row range '({start_row}, {end_row})'"
|
||||
if start_row == 1 && end_row == LAST_ROW {
|
||||
// full row selected. The cell must be at the top or the bottom of the range
|
||||
if selected_column != start_column && selected_column != end_column {
|
||||
return Err(format!(
|
||||
"The selected cell is not the column edge. Column '{selected_column}' and column range '({start_column}, {end_column})'"
|
||||
));
|
||||
}
|
||||
} else if start_column == 1 && end_column == LAST_COLUMN {
|
||||
// full column selected. The cell must be at the left or the right of the range
|
||||
if selected_row != start_row && selected_row != end_row {
|
||||
return Err(format!(
|
||||
"The selected cell is not in the row edge. Row: '{selected_row}' and row range '({start_row}, {end_row})'"
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// The selected cells must be on one of the corners of the selected range:
|
||||
if selected_row != start_row && selected_row != end_row {
|
||||
return Err(format!(
|
||||
"The selected cell is not in one of the corners. Row: '{selected_row}' and row range '({start_row}, {end_row})'"
|
||||
));
|
||||
}
|
||||
if selected_column != start_column && selected_column != end_column {
|
||||
return Err(format!(
|
||||
"The selected cells is not in one of the corners. Column '{selected_column}' and column range '({start_column}, {end_column})'"
|
||||
}
|
||||
if selected_column != start_column && selected_column != end_column {
|
||||
return Err(format!(
|
||||
"The selected cell is not in one of the corners. Column '{selected_column}' and column range '({start_column}, {end_column})'"
|
||||
));
|
||||
}
|
||||
}
|
||||
view.range = [start_row, start_column, end_row, end_column];
|
||||
}
|
||||
@@ -194,6 +210,15 @@ impl UserModel {
|
||||
return Ok(());
|
||||
};
|
||||
let [row_start, column_start, row_end, column_end] = range;
|
||||
if ["ArrowUp", "ArrowDown"].contains(&key) && row_start == 1 && row_end == LAST_ROW {
|
||||
// full column selected, nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
if ["ArrowRight", "ArrowLeft"].contains(&key) && column_start == 1 && column_end == LAST_COLUMN
|
||||
{
|
||||
// full row selected, nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match key {
|
||||
"ArrowRight" => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import Worksheet from "../Worksheet/Worksheet";
|
||||
import {
|
||||
COLUMN_WIDTH_SCALE,
|
||||
LAST_COLUMN,
|
||||
LAST_ROW,
|
||||
ROW_HEIGH_SCALE,
|
||||
} from "../WorksheetCanvas/constants";
|
||||
import type WorksheetCanvas from "../WorksheetCanvas/worksheetCanvas";
|
||||
@@ -318,6 +319,16 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => {
|
||||
workbookState.clearCutRange();
|
||||
setRedrawId((id) => id + 1);
|
||||
},
|
||||
onSelectColumn: (): void => {
|
||||
const { column } = model.getSelectedView();
|
||||
model.setSelectedRange(1, column, LAST_ROW, column);
|
||||
setRedrawId((id) => id + 1);
|
||||
},
|
||||
onSelectRow: (): void => {
|
||||
const { row } = model.getSelectedView();
|
||||
model.setSelectedRange(row, 1, row, LAST_COLUMN);
|
||||
setRedrawId((id) => id + 1);
|
||||
},
|
||||
root: rootRef,
|
||||
});
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ interface Options {
|
||||
onNextSheet: () => void;
|
||||
onPreviousSheet: () => void;
|
||||
onEscape: () => void;
|
||||
onSelectColumn: () => void;
|
||||
onSelectRow: () => void;
|
||||
root: RefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
@@ -51,6 +53,16 @@ interface Options {
|
||||
// * Ctrl+u/i/b: style
|
||||
// * Ctrl+z/y: undo/redo
|
||||
// * F2: start editing
|
||||
// * Ctrl+Space: select column
|
||||
// * Shift+Space: select row
|
||||
//
|
||||
// # Not implemented yet:
|
||||
// * Ctrl+a: select all (continuous area around the selection, if it exists,
|
||||
// otherwise select whole sheet)
|
||||
// * Ctrl+Shift+Arrows: select to edge
|
||||
// * Ctrl+Shift+Home/End: select to end
|
||||
// * Ctrl+Shift++: (after selecting) insert row/column (also Alt+I, R or C)
|
||||
// * Ctrl+-: (after selecting) delete row/column
|
||||
|
||||
// References:
|
||||
// In Google Sheets: Ctrl+/ shows the list of keyboard shortcuts
|
||||
@@ -63,6 +75,7 @@ const useKeyboardNavigation = (
|
||||
const onKeyDown = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
const { key } = event;
|
||||
const lowerKey = key.toLowerCase();
|
||||
const { root } = options;
|
||||
// Silence the linter
|
||||
if (!root.current) {
|
||||
@@ -71,45 +84,40 @@ const useKeyboardNavigation = (
|
||||
if (event.target !== root.current) {
|
||||
return;
|
||||
}
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
switch (key) {
|
||||
const isCtrl = event.metaKey || event.ctrlKey;
|
||||
const isShift = event.shiftKey;
|
||||
const isAlt = event.altKey;
|
||||
if (isCtrl && !isShift && !isAlt) {
|
||||
// Ctrl+...
|
||||
switch (lowerKey) {
|
||||
case "z": {
|
||||
if (event.shiftKey) {
|
||||
options.onRedo();
|
||||
} else {
|
||||
options.onUndo();
|
||||
}
|
||||
options.onUndo();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
break;
|
||||
}
|
||||
case "y": {
|
||||
options.onRedo();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
break;
|
||||
}
|
||||
case "b": {
|
||||
options.onBold();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
break;
|
||||
}
|
||||
case "i": {
|
||||
options.onItalic();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
break;
|
||||
}
|
||||
case "u": {
|
||||
options.onUnderline();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
break;
|
||||
}
|
||||
case "a": {
|
||||
@@ -119,18 +127,61 @@ const useKeyboardNavigation = (
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
case " ": {
|
||||
options.onSelectColumn();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
// No default
|
||||
}
|
||||
if (isNavigationKey(key)) {
|
||||
// Ctrl+Arrows, Ctrl+Home/End
|
||||
options.onNavigationToEdge(key);
|
||||
// navigate_to_edge_in_direction
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.altKey) {
|
||||
if (isCtrl && isShift && !isAlt) {
|
||||
// Ctrl+Shift+...
|
||||
switch (lowerKey) {
|
||||
case "z": {
|
||||
options.onRedo();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isShift && !isAlt && !isCtrl) {
|
||||
// Shift+...
|
||||
switch (key) {
|
||||
case " ": {
|
||||
options.onSelectRow();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
case "ArrowRight":
|
||||
case "ArrowLeft":
|
||||
case "ArrowUp":
|
||||
case "ArrowDown": {
|
||||
options.onExpandAreaSelectedKeyboard(key);
|
||||
break;
|
||||
}
|
||||
case "Tab": {
|
||||
options.onArrowLeft();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isAlt && !isCtrl && !isShift) {
|
||||
// Alt+...
|
||||
switch (key) {
|
||||
case "ArrowDown": {
|
||||
// select next sheet
|
||||
@@ -147,6 +198,12 @@ const useKeyboardNavigation = (
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// At this point we know that no modifier keys are pressed
|
||||
if (isCtrl || isShift || isAlt) {
|
||||
// If any modifier key is pressed, we do not handle the key
|
||||
return;
|
||||
}
|
||||
if (key === "F2") {
|
||||
options.onCellEditStart();
|
||||
@@ -162,67 +219,43 @@ const useKeyboardNavigation = (
|
||||
return;
|
||||
}
|
||||
// Worksheet Navigation
|
||||
if (event.shiftKey) {
|
||||
if (
|
||||
key === "ArrowRight" ||
|
||||
key === "ArrowLeft" ||
|
||||
key === "ArrowUp" ||
|
||||
key === "ArrowDown"
|
||||
) {
|
||||
options.onExpandAreaSelectedKeyboard(key);
|
||||
} else if (key === "Tab") {
|
||||
options.onArrowLeft();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (key) {
|
||||
case "ArrowRight":
|
||||
case "Tab": {
|
||||
options.onArrowRight();
|
||||
|
||||
break;
|
||||
}
|
||||
case "ArrowLeft": {
|
||||
options.onArrowLeft();
|
||||
|
||||
break;
|
||||
}
|
||||
case "ArrowDown":
|
||||
case "Enter": {
|
||||
options.onArrowDown();
|
||||
|
||||
break;
|
||||
}
|
||||
case "ArrowUp": {
|
||||
options.onArrowUp();
|
||||
|
||||
break;
|
||||
}
|
||||
case "End": {
|
||||
options.onKeyEnd();
|
||||
|
||||
break;
|
||||
}
|
||||
case "Home": {
|
||||
options.onKeyHome();
|
||||
|
||||
break;
|
||||
}
|
||||
case "Delete": {
|
||||
options.onCellsDeleted();
|
||||
|
||||
break;
|
||||
}
|
||||
case "PageDown": {
|
||||
options.onPageDown();
|
||||
|
||||
break;
|
||||
}
|
||||
case "PageUp": {
|
||||
options.onPageUp();
|
||||
|
||||
break;
|
||||
}
|
||||
case "Escape": {
|
||||
|
||||
@@ -118,7 +118,13 @@
|
||||
"delete_column": "Delete column '{{column}}'",
|
||||
"freeze": "Freeze",
|
||||
"insert_row": "Insert row",
|
||||
"insert_column": "Insert column"
|
||||
"insert_column": "Insert column",
|
||||
"move_row": "Move row",
|
||||
"move_column": "Move column",
|
||||
"move_row_up": "Move row up",
|
||||
"move_row_down": "Move row down",
|
||||
"move_column_left": "Move column left",
|
||||
"move_column_right": "Move column right"
|
||||
},
|
||||
"color_picker": {
|
||||
"apply": "Add color",
|
||||
|
||||
Reference in New Issue
Block a user