Tables: Format as Table inside a table restyles it

Home > Format as Table on a cell or range inside an existing table
failed with "A table can't overlap another table." Excel applies the
picked style to that table instead. The command now returns that table
and its range, as it does for a new table. A range crossing a table's
edge is still rejected.

Without a range, the active area of the selection decides, as it does
for Insert > Table. The native Format menu's Format as Table now runs
home.formatAsTable instead of insert.table.
This commit is contained in:
D. Fender
2026-10-22 11:43:58 -07:00
parent a1c84547e3
commit 09f78cb3a8
3 changed files with 79 additions and 3 deletions
+1 -1
View File
@@ -125,7 +125,7 @@ fn tree() -> Vec<(&'static str, Vec<Entry>)> {
("Unhide Sheet…", "sheet.unhide", None),
("-", "", None),
("Conditional Formatting…", "dialog:manageRules", None),
("Format as Table", "insert.table", None),
("Format as Table", "home.formatAsTable", None),
],
),
(
+17 -2
View File
@@ -26,9 +26,9 @@ pub fn specs() -> Vec<CommandSpec> {
"Format as Table",
["Home", "Styles"],
None,
"{range?, style: \"TableStyleMedium2\", header?}",
"{range?, style: \"TableStyleMedium2\", header?} (inside a table: restyles it)",
has_doc,
insert_table
format_as_table
),
cmd!("table.totalRow", "Total Row", ["Table", "Table Style Options"], None, "{table?, on?: bool}", has_doc, |s, p| table_flag(
s, p, "totals"
@@ -351,6 +351,21 @@ fn insert_table(s: &mut Session, p: &Json) -> Result<Json> {
})
}
/// Format as Table inside a table restyles that table, as in Excel; elsewhere it makes a new one.
fn format_as_table(s: &mut Session, p: &Json) -> Result<Json> {
// Without a range, the active area decides (as in insert_table), not the first one.
let r = if p.get("range").is_some() { target_range(s, p)? } else { s.doc()?.selection.current() };
let sheet = target_sheet(s, p)?;
let inside = s.doc()?.wb.sheet(sheet).and_then(|sh| sh.tables.iter().find(|t| t.range.contains_range(&r))).map(|t| (t.name.clone(), t.range));
match inside {
Some((table, range)) => {
table_style(s, &with(p, "table", json!(table)))?;
Ok(json!({"table": table, "range": range.a1()}))
}
None => insert_table(s, p),
}
}
fn find_table(s: &Session, p: &Json) -> Result<(usize, usize)> {
let d = s.doc()?;
if let Some(n) = str_param(p, "table") {
@@ -0,0 +1,61 @@
use gridcraft_engine::Session;
use serde_json::json;
fn table() -> Session {
let mut s = Session::new();
s.new_workbook();
s.execute("range.setValues", json!({"range":"A1", "values":[["Item", "Qty"], ["x", "7"], ["y", "8"]]})).unwrap();
s.execute("insert.table", json!({"range":"A1:B3", "header":true, "name":"T"})).unwrap();
s
}
fn styles(s: &Session) -> Vec<String> {
s.doc().unwrap().wb.active().unwrap().tables.iter().map(|t| t.style.clone()).collect()
}
#[test]
fn format_as_table_inside_a_table_restyles_it() {
let mut s = table();
s.execute("selection.set", json!({"range":"B2"})).unwrap();
s.execute("home.formatAsTable", json!({"style":"TableStyleLight9"})).unwrap();
assert_eq!(styles(&s), ["TableStyleLight9"]);
}
#[test]
fn format_as_table_on_a_range_inside_a_table_restyles_it() {
let mut s = table();
s.execute("home.formatAsTable", json!({"range":"A2:B3", "style":"TableStyleDark3"})).unwrap();
assert_eq!(styles(&s), ["TableStyleDark3"]);
}
#[test]
fn format_as_table_across_a_table_edge_is_rejected() {
let mut s = table();
let e = s.execute("home.formatAsTable", json!({"range":"A1:C3", "style":"TableStyleLight9"})).unwrap_err();
assert!(e.to_string().contains("overlap"), "{e}");
assert_eq!(styles(&s), ["TableStyleMedium2"]);
}
#[test]
fn format_as_table_inside_a_table_returns_that_table() {
let mut s = table();
let r = s.execute("home.formatAsTable", json!({"range":"B2", "style":"TableStyleLight9"})).unwrap();
assert_eq!(r, json!({"table":"T", "range":"A1:B3"}));
}
#[test]
fn format_as_table_uses_the_active_area_of_a_multi_area_selection() {
let mut s = table();
s.execute("selection.set", json!({"range":"D10:E11,B2"})).unwrap();
s.execute("home.formatAsTable", json!({"style":"TableStyleLight9"})).unwrap();
assert_eq!(styles(&s), ["TableStyleLight9"]);
}
#[test]
fn format_as_table_inside_a_table_with_data_beside_it_restyles_it() {
let mut s = table();
s.execute("range.setValues", json!({"range":"C1", "values":[["Note"], ["a"], ["b"]]})).unwrap();
s.execute("selection.set", json!({"range":"B2"})).unwrap();
s.execute("home.formatAsTable", json!({"style":"TableStyleLight9"})).unwrap();
assert_eq!(styles(&s), ["TableStyleLight9"]);
}