44 * See LICENSE for details.
55 */
66
7- import { motion , AnimatePresence } from 'framer-motion' ;
7+ import { motion , AnimatePresence , useReducedMotion } from 'framer-motion' ;
88import { Check , CaretDown , Lock , Star } from '@phosphor-icons/react' ;
99import { useTranslation } from 'react-i18next' ;
1010import { canAccessAlgorithm } from '@/services/entitlementService' ;
11+ import {
12+ menuInitial ,
13+ menuAnimate ,
14+ menuExit ,
15+ menuTransition ,
16+ getChromeTransition ,
17+ CHROME_DURATION_FAST ,
18+ } from '@/motion/chromeMotion' ;
1119
1220function AlgorithmDropdown ( {
1321 algorithms,
@@ -27,6 +35,7 @@ function AlgorithmDropdown({
2735 isAuthenticated,
2836} ) {
2937 const { t } = useTranslation ( ) ;
38+ const reduceMotion = useReducedMotion ( ) ;
3039
3140 const handleStarClick = ( event , algo ) => {
3241 event . stopPropagation ( ) ;
@@ -58,7 +67,7 @@ function AlgorithmDropdown({
5867 </ div >
5968 < motion . div
6069 animate = { { rotate : isDropdownOpen ? 180 : 0 } }
61- transition = { { duration : 0.2 } }
70+ transition = { getChromeTransition ( reduceMotion , CHROME_DURATION_FAST ) }
6271 >
6372 < CaretDown
6473 size = { 20 }
@@ -70,26 +79,21 @@ function AlgorithmDropdown({
7079 < AnimatePresence >
7180 { isDropdownOpen && (
7281 < motion . div
73- initial = { { opacity : 0 , y : - 10 } }
74- animate = { { opacity : 1 , y : 0 } }
75- exit = { { opacity : 0 , y : - 10 } }
76- transition = { { duration : 0.2 } }
82+ initial = { menuInitial ( reduceMotion ) }
83+ animate = { menuAnimate ( ) }
84+ exit = { menuExit ( reduceMotion ) }
85+ transition = { menuTransition ( reduceMotion ) }
7786 className = "absolute z-10 w-full mt-2 bg-surface-elevated border-2 border-[var(--color-border-strong)] rounded-lg shadow-xl overflow-hidden max-h-72 overflow-y-auto algo-dropdown"
7887 >
79- { algorithmGroups . map ( ( group , groupIndex ) => (
88+ { algorithmGroups . map ( group => (
8089 < div key = { group . label } >
8190 < div className = "px-4 py-2 text-xs font-bold text-text-tertiary uppercase tracking-wider sticky top-0 bg-surface-elevated z-10 border-b border-[var(--color-border-strong)]" >
8291 { group . label }
8392 </ div >
84- { group . algorithms . map ( ( algoValue , index ) => {
93+ { group . algorithms . map ( algoValue => {
8594 const algo = algorithms . find ( a => a . value === algoValue ) ;
8695 if ( ! algo ) return null ;
8796
88- const itemIndex =
89- algorithmGroups
90- . slice ( 0 , groupIndex )
91- . reduce ( ( acc , g ) => acc + g . algorithms . length , 0 ) + index ;
92-
9397 const isLocked =
9498 categoryType &&
9599 ! canAccessAlgorithm ( algo . value , categoryType , user ) ;
@@ -98,11 +102,8 @@ function AlgorithmDropdown({
98102 isFavorite ?. ( categoryType , algo . value ) ?? false ;
99103
100104 return (
101- < motion . div
105+ < div
102106 key = { algo . value }
103- initial = { { opacity : 0 , x : - 20 } }
104- animate = { { opacity : 1 , x : 0 } }
105- transition = { { delay : itemIndex * 0.03 } }
106107 className = { `group flex items-center w-full transition-colors duration-150 hover:bg-surface-elevated focus-within:bg-surface-elevated ${
107108 isSelected
108109 ? 'bg-theme-primary-light text-theme-primary dark:text-white'
@@ -139,22 +140,11 @@ function AlgorithmDropdown({
139140 </ span >
140141 </ div >
141142 { isSelected ? (
142- < motion . div
143- initial = { { scale : 0 } }
144- animate = { { scale : 1 } }
145- transition = { {
146- type : 'spring' ,
147- stiffness : 500 ,
148- damping : 25 ,
149- } }
150- className = "shrink-0 ms-2"
151- >
152- < Check
153- size = { 18 }
154- weight = "bold"
155- className = "text-[#3b82f6] dark:text-white"
156- />
157- </ motion . div >
143+ < Check
144+ size = { 18 }
145+ weight = "bold"
146+ className = "shrink-0 ms-2 text-[#3b82f6] dark:text-white"
147+ />
158148 ) : isLocked ? (
159149 < Lock
160150 size = { 18 }
@@ -193,7 +183,7 @@ function AlgorithmDropdown({
193183 />
194184 </ button >
195185 ) }
196- </ motion . div >
186+ </ div >
197187 ) ;
198188 } ) }
199189 </ div >
0 commit comments