diff --git a/CHANGELOG.md b/CHANGELOG.md index bd9a84c58..2e143ff1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Traditional Chinese (繁體中文) language in Settings > General with full UI translation +- An Add button in the table status bar inserts a new row at the end of the grid and starts editing it. ## [0.51.1] - 2026-06-16 diff --git a/TablePro/Views/Main/Child/MainEditorContentView.swift b/TablePro/Views/Main/Child/MainEditorContentView.swift index 7684bcb72..cfac91a48 100644 --- a/TablePro/Views/Main/Child/MainEditorContentView.swift +++ b/TablePro/Views/Main/Child/MainEditorContentView.swift @@ -714,7 +714,8 @@ struct MainEditorContentView: View { onRemove: { coordinator.structureActions?.removeRow?() } ), onToggleFilters: { coordinator.toggleFilterPanel() }, - onFetchAll: { coordinator.fetchAllRows() } + onFetchAll: { coordinator.fetchAllRows() }, + onAddRow: currentTabAllowsAddRow ? { onAddRow() } : nil ) } diff --git a/TablePro/Views/Main/Child/MainStatusBarView.swift b/TablePro/Views/Main/Child/MainStatusBarView.swift index f7bdf369e..bcd976109 100644 --- a/TablePro/Views/Main/Child/MainStatusBarView.swift +++ b/TablePro/Views/Main/Child/MainStatusBarView.swift @@ -41,15 +41,27 @@ struct MainStatusBarView: View { let structureState: StatusBarStructureState let onToggleFilters: () -> Void let onFetchAll: (() -> Void)? + let onAddRow: (() -> Void)? @State private var showColumnPopover = false private var isStructureMode: Bool { viewMode == .structure } private var showsDataChrome: Bool { !isStructureMode } + static func showsAddRow(viewMode: ResultsViewMode, canAddRow: Bool) -> Bool { + viewMode == .data && canAddRow + } + private var filterToggleHelp: String { - let label = String(localized: "Toggle Filters") - guard let combo = AppSettingsManager.shared.keyboard.shortcut(for: .toggleFilters), + helpText(String(localized: "Toggle Filters"), shortcut: .toggleFilters) + } + + private var addRowHelp: String { + helpText(String(localized: "Add Row"), shortcut: .addRow) + } + + private func helpText(_ label: String, shortcut action: ShortcutAction) -> String { + guard let combo = AppSettingsManager.shared.keyboard.shortcut(for: action), !combo.isCleared else { return label } @@ -141,6 +153,20 @@ struct MainStatusBarView: View { } if showsDataChrome { + if Self.showsAddRow(viewMode: viewMode, canAddRow: onAddRow != nil), let onAddRow { + Button { + onAddRow() + } label: { + HStack(spacing: 4) { + Image(systemName: "plus") + Text("Add") + } + } + .controlSize(.small) + .help(addRowHelp) + .accessibilityLabel(String(localized: "Add Row")) + } + if snapshot.hasColumns { Button { showColumnPopover.toggle() diff --git a/TableProTests/Views/Main/MainStatusBarLayoutTests.swift b/TableProTests/Views/Main/MainStatusBarLayoutTests.swift index 2887daa17..057c55045 100644 --- a/TableProTests/Views/Main/MainStatusBarLayoutTests.swift +++ b/TableProTests/Views/Main/MainStatusBarLayoutTests.swift @@ -42,8 +42,23 @@ struct MainStatusBarLayoutTests { onRemove: {} ), onToggleFilters: {}, - onFetchAll: nil + onFetchAll: nil, + onAddRow: nil ) #expect(type(of: view.body) != Never.self) } + + @Test("Add Row button shows only in Data mode when adding is allowed") + func addRowVisibilityByMode() { + #expect(MainStatusBarView.showsAddRow(viewMode: .data, canAddRow: true)) + #expect(!MainStatusBarView.showsAddRow(viewMode: .structure, canAddRow: true)) + #expect(!MainStatusBarView.showsAddRow(viewMode: .json, canAddRow: true)) + } + + @Test("Add Row button is hidden when adding is not allowed") + func addRowHiddenWhenNotAllowed() { + #expect(!MainStatusBarView.showsAddRow(viewMode: .data, canAddRow: false)) + #expect(!MainStatusBarView.showsAddRow(viewMode: .structure, canAddRow: false)) + #expect(!MainStatusBarView.showsAddRow(viewMode: .json, canAddRow: false)) + } }