fix(ui): ограничить список Filters с сохранением порядка

This commit is contained in:
Denozordec committed 2026-10-04 16:12:22 +07:00
1 parent 06850169ba
commit 4a5ae8baa1
7 files changed
+88 -19

No files matched your search

+35 -15
View File
@@ -1,7 +1,7 @@
"use client"
import { useMemo } from "react"
import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
import { useEffect, useMemo } from "react"
import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
import {
type ColumnDef,
useTable,
@@ -154,11 +154,12 @@ function FilterRowActions({
onDelete: () => void
}) {
return (
<div className="flex items-center gap-0.5 justify-end opacity-0 transition-opacity group-hover/row:opacity-100 focus-within:opacity-100">
<div className="flex items-center gap-0.5 justify-end">
<Button
type="button"
size="sm"
variant="ghost"
className="size-7 p-0 text-muted-foreground hover:text-foreground"
className="size-11 p-0 text-muted-foreground hover:text-foreground"
onClick={onEdit}
disabled={disabled}
aria-label="Редактировать правило"
@@ -166,9 +167,10 @@ function FilterRowActions({
<PencilIcon className="size-3.5" />
</Button>
<Button
type="button"
size="sm"
variant="ghost"
className="size-7 p-0 text-muted-foreground hover:text-destructive"
className="size-11 p-0 text-muted-foreground hover:text-destructive"
onClick={onDelete}
disabled={disabled}
aria-label="Удалить правило"
@@ -208,6 +210,7 @@ function FiltersDataGrid({
onMoveUp,
onMoveDown,
}: FiltersDataGridProps) {
const orderedIndex = useMemo(() => new Map(orderedRules.map((rule, index) => [rule.id, index])), [orderedRules])
const columns = useMemo<ColumnDef<DataGridFeatures, FilterRule>[]>(
() => [
{
@@ -215,7 +218,7 @@ function FiltersDataGrid({
header: () => <span className="text-xs font-medium text-muted-foreground">#</span>,
enableSorting: false,
cell: ({ row }) => {
const index = orderedRules.findIndex(r => r.id === row.original.id)
const index = orderedIndex.get(row.original.id) ?? -1
const isLast = index === orderedRules.length - 1
return (
<div className="flex items-center justify-center text-[11px] font-mono text-muted-foreground/40">
@@ -227,7 +230,7 @@ function FiltersDataGrid({
</div>
)
},
size: 28,
size: 32,
meta: { headerClassName: DATA_GRID_CELL_PAD_FIRST, cellClassName: DATA_GRID_CELL_PAD_FIRST },
},
{
@@ -235,16 +238,16 @@ function FiltersDataGrid({
header: () => <span className="sr-only">Порядок</span>,
enableSorting: false,
cell: ({ row }) => {
const index = orderedRules.findIndex(r => r.id === row.original.id)
const index = orderedIndex.get(row.original.id) ?? -1
const isLast = index === orderedRules.length - 1
return (
<div className="flex flex-col gap-px opacity-0 transition-opacity group-hover/row:opacity-100 focus-within:opacity-100">
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => onMoveUp(row.original.id)}
aria-label={`Поднять правило ${row.original.community}`}
disabled={mutationsLocked || index <= 0}
className="text-muted-foreground/50 hover:text-foreground disabled:opacity-20"
className="size-11 shrink-0 inline-flex items-center justify-center rounded text-muted-foreground hover:bg-muted disabled:opacity-40"
>
<ChevronUpIcon className="size-3" />
</button>
@@ -253,18 +256,19 @@ function FiltersDataGrid({
onClick={() => onMoveDown(row.original.id)}
aria-label={`Опустить правило ${row.original.community}`}
disabled={mutationsLocked || index < 0 || isLast}
className="text-muted-foreground/50 hover:text-foreground disabled:opacity-20"
className="size-11 shrink-0 inline-flex items-center justify-center rounded text-muted-foreground hover:bg-muted disabled:opacity-40"
>
<ChevronDownIcon className="size-3" />
</button>
</div>
)
},
size: 28,
size: 104,
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD },
},
{
id: "community",
size: 120,
accessorKey: "community",
header: ({ column }) => <DataGridSortHeader column={column} title="Community" />,
enableSorting,
@@ -301,6 +305,7 @@ function FiltersDataGrid({
},
{
id: "gateway",
size: 192,
accessorKey: "gateway",
header: ({ column }) => <DataGridSortHeader column={column} title="Gateway" />,
enableSorting,
@@ -316,6 +321,7 @@ function FiltersDataGrid({
},
{
id: "description",
size: 240,
accessorKey: "description",
header: ({ column }) => <DataGridSortHeader column={column} title="Описание" />,
enableSorting,
@@ -335,13 +341,14 @@ function FiltersDataGrid({
onDelete={() => onDelete(row.original.id)}
/>
),
size: 64,
size: 112,
meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: DATA_GRID_CELL_PAD_LAST },
},
],
[
communityNameMap,
orderedRules,
orderedIndex,
mutationsLocked,
enableSorting,
onDelete,
@@ -356,12 +363,21 @@ function FiltersDataGrid({
const table = useTable({
features: dataGridFeatures,
manualPagination: true,
manualPagination: false,
autoResetPageIndex: false,
initialState: { pagination: { pageIndex: 0, pageSize: 50 }, columnPinning: { start: ["priority", "community"], end: [] } },
manualSorting: !enableSorting,
data: rules,
columns,
getRowId: (row) => row.id,
})
const { pageIndex, pageSize } = table.state.pagination
useEffect(() => {
const lastPage = Math.max(0, Math.ceil(rules.length / pageSize) - 1)
if (pageIndex > lastPage) table.setPageIndex(lastPage)
}, [rules.length, pageIndex, pageSize, table])
if (rules.length === 0) {
return (
<EmptyState
@@ -374,7 +390,11 @@ function FiltersDataGrid({
return (
<>
<DataGridShell table={table} recordCount={rules.length} />
<DataGridShell table={table} recordCount={rules.length} pagination={rules.length > 50 || pageSize !== 50} tableLayout={{ dense: true, columnsPinnable: true }}>
<DataGridContainer border={false} className="w-full rounded-none border-0">
<DataGridScrollArea orientation="horizontal"><DataGridTable /></DataGridScrollArea>
</DataGridContainer>
</DataGridShell>
<div className="px-5 py-2 text-[11px] text-muted-foreground/40 flex items-center gap-1.5 border-t">
<StarIcon className="size-3 text-amber-400 fill-amber-400 shrink-0" />
Последнее правило имеет наивысший приоритет в RouterOS