diff --git a/frontend/src/components/format/FormatConditionsTab.jsx b/frontend/src/components/format/FormatConditionsTab.jsx index 792e812..7a0f078 100644 --- a/frontend/src/components/format/FormatConditionsTab.jsx +++ b/frontend/src/components/format/FormatConditionsTab.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback, useRef, useEffect} from 'react'; import PropTypes from 'prop-types'; import {InfoIcon} from 'lucide-react'; import {usePatterns} from '@hooks/usePatterns'; @@ -8,60 +8,113 @@ import AddButton from '@ui/DataBar/AddButton'; const FormatConditionsTab = ({conditions, onConditionsChange}) => { const {patterns, isLoading, error} = usePatterns(); + const scrollContainerRef = useRef(null); + const previousConditionsLength = useRef(conditions.length); - const handleAddCondition = () => { - onConditionsChange([createCondition(), ...conditions]); - }; - - const handleConditionChange = (index, updatedCondition) => { - const newConditions = [...conditions]; - newConditions[index] = updatedCondition; - onConditionsChange(newConditions); - }; - - const handleConditionDelete = index => { - onConditionsChange(conditions.filter((_, i) => i !== index)); - }; - - const handleMoveUp = index => { - if (index > 0) { - const newConditions = [...conditions]; - [newConditions[index - 1], newConditions[index]] = [ - newConditions[index], - newConditions[index - 1] - ]; - onConditionsChange(newConditions); + // Effect to handle scrolling when conditions are added + useEffect(() => { + if (conditions.length > previousConditionsLength.current) { + // Scroll to bottom with smooth animation + scrollContainerRef.current?.scrollTo({ + top: scrollContainerRef.current.scrollHeight, + behavior: 'smooth' + }); } - }; + previousConditionsLength.current = conditions.length; + }, [conditions.length]); - const handleMoveDown = index => { - if (index < conditions.length - 1) { - const newConditions = [...conditions]; - [newConditions[index], newConditions[index + 1]] = [ - newConditions[index + 1], - newConditions[index] - ]; - onConditionsChange(newConditions); - } - }; + // Memoized handler for adding conditions + const handleAddCondition = useCallback(() => { + const newCondition = createCondition(); + // Append to end and create a new array reference + onConditionsChange(currentConditions => [ + ...currentConditions, + newCondition + ]); + }, [onConditionsChange]); - const handleMoveToTop = index => { - if (index > 0) { - const newConditions = [...conditions]; - const [movedCondition] = newConditions.splice(index, 1); - newConditions.unshift(movedCondition); - onConditionsChange(newConditions); - } - }; + // Memoized handler for condition updates + const handleConditionChange = useCallback( + (index, updatedCondition) => { + onConditionsChange(currentConditions => { + const newConditions = [...currentConditions]; + newConditions[index] = {...updatedCondition}; // Create new reference + return newConditions; + }); + }, + [onConditionsChange] + ); - const handleMoveToBottom = index => { - if (index < conditions.length - 1) { - const newConditions = [...conditions]; - const [movedCondition] = newConditions.splice(index, 1); - newConditions.push(movedCondition); - onConditionsChange(newConditions); - } - }; + // Memoized handler for condition deletion + const handleConditionDelete = useCallback( + index => { + onConditionsChange(currentConditions => + currentConditions.filter((_, i) => i !== index) + ); + }, + [onConditionsChange] + ); + + // Memoized movement handlers + const handleMoveUp = useCallback( + index => { + if (index > 0) { + onConditionsChange(currentConditions => { + const newConditions = [...currentConditions]; + [newConditions[index - 1], newConditions[index]] = [ + {...newConditions[index]}, + {...newConditions[index - 1]} + ]; + return newConditions; + }); + } + }, + [onConditionsChange] + ); + + const handleMoveDown = useCallback( + index => { + onConditionsChange(currentConditions => { + if (index < currentConditions.length - 1) { + const newConditions = [...currentConditions]; + [newConditions[index], newConditions[index + 1]] = [ + {...newConditions[index + 1]}, + {...newConditions[index]} + ]; + return newConditions; + } + return currentConditions; + }); + }, + [onConditionsChange] + ); + + const handleMoveToTop = useCallback( + index => { + if (index > 0) { + onConditionsChange(currentConditions => { + const newConditions = [...currentConditions]; + const [movedCondition] = newConditions.splice(index, 1); + return [{...movedCondition}, ...newConditions]; + }); + } + }, + [onConditionsChange] + ); + + const handleMoveToBottom = useCallback( + index => { + onConditionsChange(currentConditions => { + if (index < currentConditions.length - 1) { + const newConditions = [...currentConditions]; + const [movedCondition] = newConditions.splice(index, 1); + return [...newConditions, {...movedCondition}]; + } + return currentConditions; + }); + }, + [onConditionsChange] + ); if (isLoading) { return ( @@ -85,14 +138,8 @@ const FormatConditionsTab = ({conditions, onConditionsChange}) => { return (
Define matching rules using required and negated @@ -100,17 +147,16 @@ const FormatConditionsTab = ({conditions, onConditionsChange}) => { releases.