feat(grid): incremental column freeze and single-column unfreeze

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-09-19 09:28:38 +08:00
1 parent 1f43d184aa
commit bb4fb6c83a
14 files changed
+108 -26

No files matched your search

+27 -4
View File
@@ -1981,7 +1981,8 @@ const {
onTableDataGridColumnOrderChanged,
frozenColumnCount,
freezeToColumn,
freezeSelectedColumns: freezeSelectedColumnsInLayout,
freezeSelectedColumnsIncrementally,
unfreezeSelectedColumns,
unfreezeAllColumns: unfreezeAllColumnsInLayout,
} = useDataGridColumnLayoutState({
columns: computed(() => props.result.columns),
@@ -2329,7 +2330,15 @@ function resetColumnOrder() {
}
function freezeSelectedColumns(selectedVisibleColumnIndexes: number[]) {
applyColumnOrderChange(() => freezeSelectedColumnsInLayout(selectedVisibleColumnIndexes));
applyColumnOrderChange(() => freezeSelectedColumnsIncrementally(selectedVisibleColumnIndexes));
}
function freezeCurrentOrSelectedColumns(selectedVisibleColumnIndexes: number[]) {
applyColumnOrderChange(() => freezeSelectedColumnsIncrementally(selectedVisibleColumnIndexes));
}
function unfreezeCurrentOrSelectedColumns(selectedVisibleColumnIndexes: number[]) {
applyColumnOrderChange(() => unfreezeSelectedColumns(selectedVisibleColumnIndexes));
}
function unfreezeAllColumns() {
@@ -11614,8 +11623,10 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
localDescending: t("grid.sortCurrentPageDescending"),
clearSort: t("grid.clearSort"),
freezeToColumn: t("grid.freezeToColumn"),
freezeSelectedColumns: t("grid.freezeSelectedColumns"),
unfreezeColumns: t("grid.unfreezeColumns"),
freezeSelectedColumns: t("grid.freezeSelectedColumns", { count: selectedColumnCount }),
freezeCurrentColumn: t("grid.freezeCurrentColumn"),
unfreezeCurrentColumn: t("grid.unfreezeCurrentColumn"),
unfreezeColumns: t("grid.unfreezeColumns", { count: frozenColumnCount.value }),
hideColumn: t("grid.hideColumn"),
hideSelectedColumns: t("grid.hideSelectedColumns", { count: selectedColumnCount }),
showAllColumnsMenu: t("grid.showAllColumnsMenu"),
@@ -11645,6 +11656,18 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
freezeSelectedColumns(selectedVisibleColumnIndexes());
clearCellSelection();
},
freezeCurrentColumn: () => {
const indexes = selectedVisibleColumnIndexes();
const idx = contextHeaderVisibleColIdx.value;
freezeCurrentOrSelectedColumns(indexes.length > 1 ? indexes : idx === null ? [] : [idx]);
clearCellSelection();
},
unfreezeCurrentColumn: () => {
const indexes = selectedVisibleColumnIndexes();
const idx = contextHeaderVisibleColIdx.value;
unfreezeCurrentOrSelectedColumns(indexes.length > 1 ? indexes : idx === null ? [] : [idx]);
clearCellSelection();
},
unfreezeColumns: () => {
unfreezeAllColumns();
clearCellSelection();
@@ -299,7 +299,7 @@ describe("DataGrid context menu target lifecycle", () => {
await settle();
expect(contextMenuLabels()).toContain("Copy Selected Column Names (2)");
expect(contextMenuLabels()).toContain("Freeze Selected Columns");
expect(contextMenuLabels()).toContain("Freeze Selected Columns (2)");
});
it("offers snapshots for full-column selections from the column header", async () => {
@@ -297,6 +297,38 @@ export function useDataGridColumnLayoutState(options: {
setFrozenColumnCount(selectedActualIdxs.length);
}
function freezeSelectedColumnsIncrementally(selectedVisibleColIdxs: number[]) {
if (selectedVisibleColIdxs.length === 0) return;
const visibleIdxs = visibleColumnIndexes.value;
const selectedActualIdxs = [...new Set(selectedVisibleColIdxs.map((vIdx) => visibleIdxs[vIdx]).filter((idx): idx is number => idx !== undefined))];
if (selectedActualIdxs.length === 0) return;
const currentOrder = orderedDisplayableColumnIndexes.value;
const frozenVisibleIndexes = visibleColumnIndexes.value.slice(0, frozenColumnCount.value);
const frozenSet = new Set(frozenVisibleIndexes);
const additions = selectedActualIdxs.filter((idx) => !frozenSet.has(idx));
if (additions.length === 0) return;
if (columnOrderSnapshotBeforeFreeze.value === null) columnOrderSnapshotBeforeFreeze.value = [...persistedColumnOrderKeys.value];
const remaining = currentOrder.filter((idx) => !additions.includes(idx));
const frozenEnd = frozenVisibleIndexes.reduce((end, idx) => Math.max(end, remaining.indexOf(idx) + 1), 0);
persistColumnOrder([...remaining.slice(0, frozenEnd), ...additions, ...remaining.slice(frozenEnd)]);
setFrozenColumnCount(frozenColumnCount.value + additions.length);
}
function unfreezeSelectedColumns(selectedVisibleColIdxs: number[]) {
if (selectedVisibleColIdxs.length === 0 || frozenColumnCount.value === 0) return;
const visibleIdxs = visibleColumnIndexes.value;
const selected = new Set(selectedVisibleColIdxs.map((vIdx) => visibleIdxs[vIdx]).filter((idx): idx is number => idx !== undefined));
const currentOrder = orderedDisplayableColumnIndexes.value;
const frozen = visibleColumnIndexes.value.slice(0, frozenColumnCount.value);
const removing = frozen.filter((idx) => selected.has(idx));
if (removing.length === 0) return;
if (columnOrderSnapshotBeforeFreeze.value === null) columnOrderSnapshotBeforeFreeze.value = [...persistedColumnOrderKeys.value];
const nextFrozen = frozen.filter((idx) => !selected.has(idx));
persistColumnOrder(currentOrder.filter((idx) => !removing.includes(idx)).concat(removing));
setFrozenColumnCount(nextFrozen.length);
if (nextFrozen.length === 0) unfreezeAllColumns();
}
function unfreezeAllColumns() {
setFrozenColumnCount(0);
if (columnOrderSnapshotBeforeFreeze.value !== null) {
@@ -441,6 +473,8 @@ export function useDataGridColumnLayoutState(options: {
frozenColumnCount,
freezeToColumn,
freezeSelectedColumns,
freezeSelectedColumnsIncrementally,
unfreezeSelectedColumns,
unfreezeAllColumns,
};
}
+4 -2
View File
@@ -2048,8 +2048,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "Verilənlər bazasını azalan sıra ilə sırala",
clearSort: "Sıralamanı təmizlə",
freezeToColumn: "Bu sütuna qədər sabitlə",
freezeSelectedColumns: "Seçilmiş sütunları sabitlə",
unfreezeColumns: "Sütunların sabitlənməsini ləğv et",
freezeSelectedColumns: "Seçilmiş sütunları sabitlə ({count})",
freezeCurrentColumn: "Cari sütunu sabitlə",
unfreezeCurrentColumn: "Cari sütunun sabitini aç",
unfreezeColumns: "Sütunların sabitlənməsini ləğv et ({count})",
hideColumn: "Bu sütunu gizlət",
hideSelectedColumns: "Seçilmiş sütunları gizlət ({count})",
showAllColumnsMenu: "Bütün sütunları göstər",
+4 -2
View File
@@ -2199,8 +2199,10 @@ export default {
sortDatabaseDescending: "Sort Database Descending",
clearSort: "Clear Sort",
freezeToColumn: "Freeze to This Column",
freezeSelectedColumns: "Freeze Selected Columns",
unfreezeColumns: "Unfreeze Columns",
freezeSelectedColumns: "Freeze Selected Columns ({count})",
freezeCurrentColumn: "Freeze Current Column",
unfreezeCurrentColumn: "Unfreeze Current Column",
unfreezeColumns: "Unfreeze Columns ({count})",
hideColumn: "Hide This Column",
hideSelectedColumns: "Hide Selected Columns ({count})",
showAllColumnsMenu: "Show All Columns",
+4 -2
View File
@@ -2047,8 +2047,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "Ordenar base de datos descendente",
clearSort: "Limpiar orden",
freezeToColumn: "Inmovilizar hasta esta columna",
freezeSelectedColumns: "Inmovilizar columnas seleccionadas",
unfreezeColumns: "Desinmovilizar columnas",
freezeSelectedColumns: "Inmovilizar columnas seleccionadas ({count})",
freezeCurrentColumn: "Inmovilizar columna actual",
unfreezeCurrentColumn: "Desinmovilizar columna actual",
unfreezeColumns: "Desinmovilizar columnas ({count})",
hideColumn: "Ocultar esta columna",
hideSelectedColumns: "Ocultar columnas seleccionadas ({count})",
showAllColumnsMenu: "Mostrar todas las columnas",
+4 -2
View File
@@ -2045,8 +2045,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "Ordina database decrescente",
clearSort: "Rimuovi Ordinamento",
freezeToColumn: "Blocca fino a questa colonna",
freezeSelectedColumns: "Blocca colonne selezionate",
unfreezeColumns: "Sblocca colonne",
freezeSelectedColumns: "Blocca colonne selezionate ({count})",
freezeCurrentColumn: "Blocca colonna corrente",
unfreezeCurrentColumn: "Sblocca colonna corrente",
unfreezeColumns: "Sblocca colonne ({count})",
hideColumn: "Nascondi questa colonna",
hideSelectedColumns: "Nascondi colonne selezionate ({count})",
showAllColumnsMenu: "Mostra tutte le colonne",
+4 -2
View File
@@ -2057,8 +2057,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "データベースで降順に並び替え",
clearSort: "並び替えをクリア",
freezeToColumn: "この列まで固定",
freezeSelectedColumns: "選択列を固定",
unfreezeColumns: "列の固定を解除",
freezeSelectedColumns: "選択列を固定 ({count})",
freezeCurrentColumn: "現在の列を固定",
unfreezeCurrentColumn: "現在の列の固定を解除",
unfreezeColumns: "列の固定を解除 ({count})",
hideColumn: "この列を非表示",
hideSelectedColumns: "選択した列を非表示 ({count})",
showAllColumnsMenu: "すべての列を表示",
+4 -2
View File
@@ -2038,8 +2038,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "데이터베이스 내림차순 정렬",
clearSort: "정렬 지우기",
freezeToColumn: "이 컬럼까지 고정",
freezeSelectedColumns: "선택한 컬럼 고정",
unfreezeColumns: "컬럼 고정 해제",
freezeSelectedColumns: "선택한 컬럼 고정 ({count})",
freezeCurrentColumn: "현재 열 고정",
unfreezeCurrentColumn: "현재 열 고정 해제",
unfreezeColumns: "컬럼 고정 해제 ({count})",
hideColumn: "이 컬럼 숨기기",
hideSelectedColumns: "선택한 컬럼 숨기기 ({count})",
showAllColumnsMenu: "모든 컬럼 표시",
+4 -2
View File
@@ -2047,8 +2047,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "Ordenar Banco de Dados Decrescente",
clearSort: "Limpar Ordenação",
freezeToColumn: "Congelar até esta coluna",
freezeSelectedColumns: "Congelar colunas selecionadas",
unfreezeColumns: "Descongelar colunas",
freezeSelectedColumns: "Congelar colunas selecionadas ({count})",
freezeCurrentColumn: "Congelar coluna atual",
unfreezeCurrentColumn: "Descongelar coluna atual",
unfreezeColumns: "Descongelar colunas ({count})",
hideColumn: "Ocultar esta coluna",
hideSelectedColumns: "Ocultar colunas selecionadas ({count})",
showAllColumnsMenu: "Mostrar todas as colunas",
+4 -2
View File
@@ -2043,8 +2043,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "Veritabanında Azalan Sırala",
clearSort: "Sıralamayı Temizle",
freezeToColumn: "Bu Sütuna Kadar Dondur",
freezeSelectedColumns: "Seçili Sütunları Dondur",
unfreezeColumns: "Sütun Dondurmayı Kaldır",
freezeSelectedColumns: "Seçili Sütunları Dondur ({count})",
freezeCurrentColumn: "Geçerli sütunu sabitle",
unfreezeCurrentColumn: "Geçerli sütunun sabitlemesini kaldır",
unfreezeColumns: "Sütun Dondurmayı Kaldır ({count})",
hideColumn: "Bu Sütunu Gizle",
hideSelectedColumns: "Seçili Sütunları Gizle ({count})",
showAllColumnsMenu: "Tüm Sütunları Göster",
+4 -2
View File
@@ -2114,8 +2114,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "数据库降序排序",
clearSort: "清除排序",
freezeToColumn: "冻结到此列",
freezeSelectedColumns: "冻结选中列",
unfreezeColumns: "取消冻结",
freezeSelectedColumns: "冻结选中列({count} 列)",
freezeCurrentColumn: "冻结当前列",
unfreezeCurrentColumn: "取消冻结当前列",
unfreezeColumns: "取消冻结({count} 列)",
hideColumn: "隐藏此列",
hideSelectedColumns: "隐藏选中列({count} 列)",
showAllColumnsMenu: "显示全部列",
+4 -2
View File
@@ -2048,8 +2048,10 @@ export default withEnglishFallback({
sortDatabaseDescending: "資料庫遞減排序",
clearSort: "清除排序",
freezeToColumn: "凍結到此欄",
freezeSelectedColumns: "凍結選取欄",
unfreezeColumns: "取消凍結",
freezeSelectedColumns: "凍結選取欄({count} 欄)",
freezeCurrentColumn: "凍結目前欄",
unfreezeCurrentColumn: "取消凍結目前欄",
unfreezeColumns: "取消凍結({count} 欄)",
hideColumn: "隱藏此欄",
hideSelectedColumns: "隱藏選取欄({count} 欄)",
showAllColumnsMenu: "顯示全部欄",
@@ -90,7 +90,8 @@ export function createDataGridColumnContextMenuItems(options: {
labels: Record<
"copyName" | "copyNames" | "details" | "copyAlterSql" | "databaseAscending" | "databaseDescending" | "localAscending" | "localDescending" | "clearSort" | "freezeToColumn" | "freezeSelectedColumns" | "unfreezeColumns" | "hideColumn" | "hideSelectedColumns" | "showAllColumnsMenu",
string
>;
> &
Partial<Record<"freezeCurrentColumn" | "unfreezeCurrentColumn", string>>;
icons: Pick<DataGridContextMenuIcons, "copy" | "columnDetails" | "database" | "ascending" | "descending" | "clearSort">;
actions: {
copyName: () => void;
@@ -100,6 +101,8 @@ export function createDataGridColumnContextMenuItems(options: {
sort: (direction: "asc" | "desc" | null, mode: "database" | "local") => void;
freezeToColumn: () => void;
freezeSelectedColumns: () => void;
freezeCurrentColumn?: () => void;
unfreezeCurrentColumn?: () => void;
unfreezeColumns: () => void;
hideColumn: () => void;
hideSelectedColumns: () => void;
@@ -142,11 +145,13 @@ export function createDataGridColumnContextMenuItems(options: {
items.push({ label: options.labels.showAllColumnsMenu, action: options.actions.showAllColumnsMenu });
}
if ((options.frozenColumnCount ?? 0) > 0) {
if ((options.contextVisibleColIdx ?? -1) < (options.frozenColumnCount ?? 0) && options.labels.unfreezeCurrentColumn && options.actions.unfreezeCurrentColumn) items.push({ label: options.labels.unfreezeCurrentColumn, action: options.actions.unfreezeCurrentColumn });
items.push({ label: options.labels.unfreezeColumns, action: options.actions.unfreezeColumns });
}
if (options.hasColumnSelection) {
items.push({ label: options.labels.freezeSelectedColumns, action: options.actions.freezeSelectedColumns });
}
if (options.labels.freezeCurrentColumn && options.actions.freezeCurrentColumn) items.push({ label: options.labels.freezeCurrentColumn, action: options.actions.freezeCurrentColumn });
items.push({ label: options.labels.freezeToColumn, action: options.actions.freezeToColumn });
}
return items;