Insert > Chart, Data > Pivot table, filters and sorts with nothing selected read the block of filled cells around the active cell, which in a region runs into the label line above the table: the chart took the label as its header row, and a pivot the label as its column names. In a region (on its label too), the table around the active cell is now the region's table, from its header row down; elsewhere it is the block of data as before. The notebook docs say so, and that Go to takes nu.r1.
- commit
- 80df9677424d34d857ff143dbe7dcf58bae45ad5
- parent
- 42069ef32f7b62d4e5d03905b79e32dccc981d65
- author
- Dami <dami.etoile@gmail.com>
- kind
- fix risk score 1.9 of 3, Jev's estimate
| M | docs/ | 5 | ++- |
| M | internal/ | 6 | +- |
| M | internal/ | 19 | +++++++- |
| M | internal/ | 68 | +++++++++++++++++++++++++----- |
@@ -67,7 +67,10 @@ A region is still cells of the grid. Its values are
6767 [typed](types.md) (file sizes in the Size format, durations, dates),6868 formulas read them, and you can format them, filter them6969 (**Data > Create a filter**), sort them (the sort is kept, so the table70-stays sorted when it runs again) and chart them. Like an70+stays sorted when it runs again), chart them and make pivot tables of71+them: from any of its cells, or its label, with nothing selected, those72+commands take the region's table, from its header row down. Go to73+(Ctrl+G) takes a region's name as formulas write it (`nu.r1`). Like an7174 [array's spill](../formulas/arrays.md#spilled-cells), they can't be7275 typed over: change the command, or freeze the region. Copies paste7376 values; downloads and XLSX hold the values; undo takes back a run (the@@ -14,8 +14,8 @@ import (
1414 "github.com/FelineStateMachine/012/internal/ui/theme"1515 )1616 17-// Charts follow Sheets: Insert > Chart charts the selection (or the block18-// of data around the active cell) and opens the chart editor, a bar on the17+// Charts follow Sheets: Insert > Chart charts the selection (or the table18+// around the active cell) and opens the chart editor, a bar on the1919 // context line for the type, series direction, headers, range and title.2020 // Charts float over the grid anchored at a cell, redraw as their data2121 // recalculates, and can be dragged, resized from the corner, and deleted@@ -89,7 +89,7 @@ func (m *Model) targetChart() int {
8989 func (m *Model) insertChart() tea.Cmd {9090 r := m.selection()9191 if !m.hasRange() {92- r = m.sheet.Region(m.cur)92+ r = m.tableAround(m.cur)9393 }9494 if used, ok := m.sheet.UsedRange(); !ok || r.From.Col > used.To.Col || r.From.Row > used.To.Row {9595 m.note = "Select the data to chart first"@@ -56,11 +56,11 @@ func (m *Model) createFilter() tea.Cmd {
5656 }5757 5858 // dataRange is the range data commands act on: the selection when there59-// is one, cut to the data in it, or else the block of data around the60-// active cell.59+// is one, cut to the data in it, or else the table around the active60+// cell.6161 func (g *grid) dataRange() sheet.Rect {6262 if !g.hasRange() {63- return g.sheet.Region(g.cur)63+ return g.tableAround(g.cur)6464 }6565 r := g.selection()6666 if used, ok := g.sheet.UsedRange(); ok {@@ -70,6 +70,19 @@ func (g *grid) dataRange() sheet.Rect {
7070 return r7171 }7272 73+// tableAround is the table around a: in a region (on its label line74+// too), the region's table from its header row down, which the block of75+// data around a would run into the label line above it; elsewhere the76+// block of data around a (Sheet.Region).77+func (g *grid) tableAround(a sheet.Addr) sheet.Rect {78+ if reg, _, ok := g.sheet.RegionAt(a); ok {79+ if t, ok := g.sheet.RegionTable(reg.Name); ok {80+ return t81+ }82+ }83+ return g.sheet.Region(a)84+}85+7386 // filterMark is the filter button in column c's header, if c is in the7487 // filter's range, and whether the column's filter hides anything.7588 func (g *grid) filterMark(c int) (string, bool) {@@ -1,33 +1,44 @@
11 package ui22 33 import (4+ "strconv"45 "testing"56 67 "github.com/FelineStateMachine/012/internal/sheet"78 )89 9-// Go to takes a region's name as formulas write it and selects its10-// table, header row included, on whichever sheet holds it.11-func TestGotoRegion(t *testing.T) {12- m := newModel()10+// withRegion adds a notebook sheet after the one shown with region name11+// showing a table of a header row and two rows of numbers, and returns12+// the sheet and the table.13+func withRegion(t *testing.T, m *Model, name string) (*sheet.Sheet, sheet.Rect) {14+ t.Helper()1315 nb, err := m.book().AddNotebook("Shell", m.sheet)1416 if err != nil {1517 t.Fatal(err)1618 }17- if err := nb.AddRegion(sheet.Region{Name: "totals", Command: "ls"}); err != nil {19+ if err := nb.AddRegion(sheet.Region{Name: name, Command: "ls"}); err != nil {1820 t.Fatal(err)1921 }20- d := &sheet.RegionData{Rows: 3, Cols: 2, Values: make([]sheet.Value, 6)}21- for i := range d.Values {22- d.Values[i] = sheet.Value{Kind: sheet.Text, Str: "x"}23- }24- if err := nb.ShowRegion("totals", d); err != nil {22+ d := &sheet.RegionData{Rows: 3, Cols: 2, Values: []sheet.Value{23+ {Kind: sheet.Text, Str: "name"}, {Kind: sheet.Text, Str: "size"},24+ {Kind: sheet.Text, Str: "a"}, {Kind: sheet.Number, Num: 1},25+ {Kind: sheet.Text, Str: "b"}, {Kind: sheet.Number, Num: 2},26+ }}27+ if err := nb.ShowRegion(name, d); err != nil {2528 t.Fatal(err)2629 }27- want, ok := nb.RegionTable("totals")30+ table, ok := nb.RegionTable(name)2831 if !ok {2932 t.Fatal("no table")3033 }34+ return nb, table35+}36+37+// Go to takes a region's name as formulas write it and selects its38+// table, header row included, on whichever sheet holds it.39+func TestGotoRegion(t *testing.T) {40+ m := newModel()41+ nb, want := withRegion(t, m, "totals")3142 press(t, m, "<ctrl+g>", "NU.Totals", "<enter>")3243 if m.mode != modeReady || m.sheet != nb {3344 t.Fatalf("mode %v, sheet %q, err %q", m.mode, m.sheet.Name(), m.errMsg)@@ -40,3 +51,38 @@ func TestGotoRegion(t *testing.T) {
4051 t.Errorf("an unknown region: mode %v", m.mode)4152 }4253 }54+55+// From a cell of a region, or its label, the table around it is the56+// region's table, not the block of data that takes in the label line;57+// Insert > Chart and a pivot table read it.58+func TestTableAroundRegion(t *testing.T) {59+ m := newModel()60+ nb, table := withRegion(t, m, "r1")61+ m.showSheet(nb)62+ label := sheet.Addr{Col: table.From.Col, Row: table.From.Row - 1}63+ for _, at := range []sheet.Addr{table.To, label} {64+ m.clearSelection()65+ m.cur = at66+ if got := m.dataRange(); got != table {67+ t.Errorf("from %v: %v, want the table %v", at, got, table)68+ }69+ if got, ok := m.pivotData(); !ok || got != table {70+ t.Errorf("from %v: pivot reads %v %v, want %v", at, got, ok, table)71+ }72+ }73+ m.cur = table.To74+ m.runCommand("insert.chart")75+ press(t, m, "<enter>")76+ if cs := nb.Charts(); len(cs) != 1 || cs[0].Data != table {77+ t.Fatalf("charts %+v, want one of %v", cs, table)78+ }79+ // Ordinary data is still the block around the cell.80+ m.showSheet(m.book().Sheets()[0])81+ for r := range 3 {82+ m.sheet.Set(sheet.Addr{Row: r}, strconv.Itoa(r))83+ }84+ m.cur = sheet.Addr{Row: 1}85+ if got := m.dataRange(); got != (sheet.Rect{To: sheet.Addr{Row: 2}}) {86+ t.Errorf("ordinary data: %v", got)87+ }88+}