fix(grid): keep save actions visible

This commit is contained in:
t8y2
2026-05-18 17:46:42 +08:00
parent b6bf3009f8
commit cf86d7f3b4
3 changed files with 87 additions and 5 deletions
+12 -4
View File
@@ -103,7 +103,7 @@ import {
} from "@/lib/columnFormatter";
import { isCancelSearchShortcut, isFocusSearchShortcut } from "@/lib/keyboardShortcuts";
import { dataGridHeaderContentWidth, scrollbarGutterWidth } from "@/lib/dataGridScrollGutter";
import { dataGridSaveActionMode } from "@/lib/dataGridSaveUi";
import { dataGridSaveActionMode, dataGridSaveToolbarState } from "@/lib/dataGridSaveUi";
import { appendColumnValueFilterCondition, buildColumnValueFilterCondition } from "@/lib/dataGridColumnFilter";
import {
filterColumnVisibilityOptions,
@@ -1335,6 +1335,14 @@ const saveActionMode = computed(() =>
useTransaction: !!useTransaction.value,
}),
);
const saveToolbarState = computed(() =>
dataGridSaveToolbarState({
editable: props.editable,
hasSaveTarget: !!props.tableMeta || !!props.customSave,
hasPendingChanges: hasPendingChanges.value,
isSaving: isSaving.value,
}),
);
function canEditRowItem(item: RowItem | undefined): boolean {
return !!props.editable && !!item && !item.isDeleted && (item.isNew || canEditExistingRows.value);
@@ -2711,14 +2719,14 @@ defineExpose({
<span class="h-1.5 w-1.5 rounded-full bg-emerald-500 animate-pulse" />
{{ t("grid.transactionActive") }}
</span>
<template v-if="editable && (tableMeta || customSave) && hasPendingChanges">
<template v-if="saveToolbarState.showActions">
<Tooltip>
<TooltipTrigger as-child>
<Button
variant="default"
size="sm"
class="h-5 text-xs px-1.5 shrink-0"
:disabled="isSaving"
:disabled="saveToolbarState.actionsDisabled"
@click="onToolbarCommit"
>
<Loader2 v-if="isSaving" class="w-3 h-3 mr-1 animate-spin" />
@@ -2734,7 +2742,7 @@ defineExpose({
variant="outline"
size="sm"
class="h-5 text-xs px-1.5 shrink-0"
:disabled="isSaving"
:disabled="saveToolbarState.actionsDisabled"
@click="useTransaction ? onToolbarRollback() : discardChanges()"
>
<RotateCcw class="w-3 h-3 mr-1" />
+18
View File
@@ -14,3 +14,21 @@ export function dataGridSaveActionMode(options: {
secondaryActionKey: options.useTransaction ? "grid.rollback" : "grid.discard",
};
}
export interface DataGridSaveToolbarState {
showActions: boolean;
actionsDisabled: boolean;
}
export function dataGridSaveToolbarState(options: {
editable: boolean;
hasSaveTarget: boolean;
hasPendingChanges: boolean;
isSaving: boolean;
}): DataGridSaveToolbarState {
const showActions = options.editable && options.hasSaveTarget;
return {
showActions,
actionsDisabled: !showActions || !options.hasPendingChanges || options.isSaving,
};
}
+57 -1
View File
@@ -1,6 +1,6 @@
import { strict as assert } from "node:assert";
import test from "node:test";
import { dataGridSaveActionMode } from "../../apps/desktop/src/lib/dataGridSaveUi.ts";
import { dataGridSaveActionMode, dataGridSaveToolbarState } from "../../apps/desktop/src/lib/dataGridSaveUi.ts";
test("uses concise save and commit labels for the primary grid save action", () => {
assert.deepEqual(dataGridSaveActionMode({ pendingChangeCount: 3, useTransaction: true }), {
@@ -15,3 +15,59 @@ test("uses concise save and commit labels for the primary grid save action", ()
secondaryActionKey: "grid.discard",
});
});
test("keeps grid save actions visible for editable grids even without pending changes", () => {
assert.deepEqual(
dataGridSaveToolbarState({
editable: true,
hasSaveTarget: true,
hasPendingChanges: false,
isSaving: false,
}),
{
showActions: true,
actionsDisabled: true,
},
);
assert.deepEqual(
dataGridSaveToolbarState({
editable: true,
hasSaveTarget: true,
hasPendingChanges: true,
isSaving: false,
}),
{
showActions: true,
actionsDisabled: false,
},
);
});
test("hides grid save actions when a grid cannot save edits", () => {
assert.deepEqual(
dataGridSaveToolbarState({
editable: false,
hasSaveTarget: true,
hasPendingChanges: false,
isSaving: false,
}),
{
showActions: false,
actionsDisabled: true,
},
);
assert.deepEqual(
dataGridSaveToolbarState({
editable: true,
hasSaveTarget: false,
hasPendingChanges: true,
isSaving: false,
}),
{
showActions: false,
actionsDisabled: true,
},
);
});