From 0a771996424173379e513f49069ee219555dafa5 Mon Sep 17 00:00:00 2001 From: tlartem Date: Mon, 28 Jul 2025 07:52:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D1=82=D0=B0=D1=81?= =?UTF-8?q?=D0=BA=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=8D=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=BE=D0=BD=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StandardsElement/StandardsSelector.tsx | 173 +++++++++++++++++- 1 file changed, 167 insertions(+), 6 deletions(-) diff --git a/src/entitiy/element/model/implementations/StandardsElement/StandardsSelector.tsx b/src/entitiy/element/model/implementations/StandardsElement/StandardsSelector.tsx index 2f84f05..fca8a02 100644 --- a/src/entitiy/element/model/implementations/StandardsElement/StandardsSelector.tsx +++ b/src/entitiy/element/model/implementations/StandardsElement/StandardsSelector.tsx @@ -9,10 +9,28 @@ import { import { Input } from '@/component/ui/input' import { Separator } from '@/component/ui/separator' import { useToast } from '@/lib/hooks/useToast' +import { + DndContext, + DragEndEvent, + KeyboardSensor, + PointerSensor, + closestCenter, + useSensor, + useSensors, +} from '@dnd-kit/core' +import { + SortableContext, + arrayMove, + sortableKeyboardCoordinates, + useSortable, + verticalListSortingStrategy, +} from '@dnd-kit/sortable' +import { CSS } from '@dnd-kit/utilities' import { Check, ExternalLink, FileText, + GripVertical, Search, Settings, X, @@ -21,6 +39,89 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { useStandards } from './hooks' import { Standard } from './types' +// Компонент для сортируемого элемента эталона +interface SortableStandardItemProps { + standard: Standard + onRemove: (standardId: string) => void + isDragActive: boolean +} + +function SortableStandardItem({ + standard, + onRemove, + isDragActive, +}: SortableStandardItemProps) { + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ id: standard.id }) + + const style = { + transform: CSS.Transform.toString(transform), + transition: isDragging ? 'none' : transition, + opacity: isDragging ? 0.9 : 1, + zIndex: isDragging ? 1000 : 'auto', + } + + return ( +
+
+ +
+ +
+
+
+ {standard.protocol_name} +
+
+ {standard.name} +
+
+
+ +
+
+ + {standard.registry_number} +
+ + {standard.range} + +
+ + +
+ ) +} + interface StandardsSelectorProps { isOpen: boolean onClose: () => void @@ -37,12 +138,51 @@ export function StandardsSelector({ maxItems = 7, // Значение по умолчанию }: StandardsSelectorProps) { const [selectedStandards, setSelectedStandards] = useState(value) + const [isDragActive, setIsDragActive] = useState(false) + + // Синхронизируем selectedStandards с value при изменении value + useEffect(() => { + setSelectedStandards(value) + }, [value]) const [searchTerm, setSearchTerm] = useState('') const { error } = useToast() const searchInputRef = useRef(null) const { data: standards = [], isLoading, error: apiError } = useStandards() + // Настройка сенсоров для drag-and-drop + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { + distance: 10, // Начинать перетаскивание после движения на 10px + tolerance: 5, + delay: 100, + }, + }), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ) + + // Обработчик начала перетаскивания + const handleDragStart = () => { + setIsDragActive(true) + } + + // Обработчик завершения перетаскивания + const handleDragEnd = (event: DragEndEvent) => { + const { active, over } = event + setIsDragActive(false) + + if (over && active.id !== over.id) { + setSelectedStandards(items => { + const oldIndex = items.indexOf(active.id as string) + const newIndex = items.indexOf(over.id as string) + return arrayMove(items, oldIndex, newIndex) + }) + } + } + // Автофокус на поле поиска при открытии useEffect(() => { if (isOpen && searchInputRef.current) { @@ -119,9 +259,10 @@ export function StandardsSelector({ ) }, [searchTerm, standards]) - const selectedStandardsData = standards.filter(standard => - selectedStandards.includes(standard.id) - ) + // Создаем массив выбранных эталонов в том порядке, как они выбраны + const selectedStandardsData = selectedStandards + .map(standardId => standards.find(standard => standard.id === standardId)) + .filter((standard): standard is Standard => standard !== undefined) const unselectedStandards = filteredStandards.filter( standard => !selectedStandards.includes(standard.id) @@ -346,11 +487,31 @@ export function StandardsSelector({

Выбранные эталоны ({selectedStandardsData.length}) + + Перетащите для изменения порядка +

- {selectedStandardsData.map(standard => - renderStandardCard(standard, true, true) - )} + + + {selectedStandardsData.map(standard => ( + + ))} + +
)}