fix(ui): ограничить список Filters с сохранением порядка
This commit is contained in:
1 parent
06850169ba
commit
4a5ae8baa1
7 files changed
+88
-19
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user