1- import { useState , useCallback , useEffect , useRef } from 'react' ;
1+ import { useState , useCallback , useEffect , useLayoutEffect , useRef } from 'react' ;
22import {
33 Archive ,
44 Search ,
@@ -29,6 +29,8 @@ import { dispatchCommand } from '../hooks/useCommandRegistry';
2929import { IconButton } from '../ui/primitives' ;
3030import { noteListNavDirection } from '../utils/noteListKeys' ;
3131import { modAccel } from '../utils/modAccel' ;
32+ import { playListEnter , playMotion } from '../motion/gsapRuntime' ;
33+ import { elementsForNoteIds , planListEnter } from '../motion/listEnter' ;
3234import type { QuickFilterType } from './sidebar' ;
3335import { NoteListContextMenu } from './NoteListContextMenu' ;
3436import { NotebookPicker } from './NotebookPicker' ;
@@ -178,12 +180,41 @@ export function NoteList({
178180 const { data : notebooks = [ ] } = useNotebookList ( ) ;
179181 const { data : notebook } = useNotebook ( selectedNotebookId ) ;
180182 const listItemsRef = useRef < HTMLUListElement | null > ( null ) ;
183+ const seenNoteIdsRef = useRef ( new Set < string > ( ) ) ;
184+ const prevSelectedIdRef = useRef < string | null > ( null ) ;
181185
182186 useEffect ( ( ) => {
183187 const selected = listItemsRef . current ?. querySelector ( '[aria-selected="true"]' ) ;
184188 selected ?. scrollIntoView ( { block : 'nearest' } ) ;
185189 } , [ selectedId ] ) ;
186190
191+ const noteIdsKey = notes . map ( n => n . id ) . join ( '\0' ) ;
192+
193+ useLayoutEffect ( ( ) => {
194+ const noteIds = noteIdsKey ? noteIdsKey . split ( '\0' ) : [ ] ;
195+ if ( ! listItemsRef . current ) {
196+ if ( noteIds . length === 0 ) seenNoteIdsRef . current = new Set ( ) ;
197+ return ;
198+ }
199+
200+ const plan = planListEnter ( {
201+ noteIds,
202+ seenIds : seenNoteIdsRef . current ,
203+ } ) ;
204+ seenNoteIdsRef . current = new Set ( noteIds ) ;
205+
206+ if ( plan . mode !== 'none' ) {
207+ playListEnter ( elementsForNoteIds ( plan . ids ) ) ;
208+ prevSelectedIdRef . current = selectedId ;
209+ return ;
210+ }
211+
212+ if ( selectedId && selectedId !== prevSelectedIdRef . current ) {
213+ playMotion ( 'list-select' , document . getElementById ( `note-${ selectedId } ` ) ) ;
214+ }
215+ prevSelectedIdRef . current = selectedId ;
216+ } , [ noteIdsKey , selectedId , isLoading ] ) ;
217+
187218 useEffect ( ( ) => {
188219 const onKeyDown = ( event : KeyboardEvent ) => {
189220 const direction = noteListNavDirection ( event ) ;
@@ -349,11 +380,10 @@ export function NoteList({
349380 aria-label = "Notes"
350381 aria-activedescendant = { selectedId ? `note-${ selectedId } ` : undefined }
351382 >
352- { notes . map ( ( note , index ) => (
383+ { notes . map ( note => (
353384 < NoteListItem
354385 key = { note . id }
355386 note = { note }
356- index = { index }
357387 isSelected = { note . id === selectedId }
358388 onSelect = { onSelect }
359389 onTagClick = { onTagClick }
@@ -412,7 +442,6 @@ export function NoteList({
412442
413443interface NoteListItemProps {
414444 note : NoteWithExcerpt ;
415- index : number ;
416445 isSelected : boolean ;
417446 onSelect : ( id : string ) => void ;
418447 onTagClick : ( tag : string ) => void ;
@@ -421,7 +450,6 @@ interface NoteListItemProps {
421450
422451function NoteListItem ( {
423452 note,
424- index,
425453 isSelected,
426454 onSelect,
427455 onTagClick,
@@ -453,7 +481,6 @@ function NoteListItem({
453481 role = "option"
454482 aria-selected = { isSelected }
455483 className = { sc ( 'note-list-item' , isSelected && 'selected' ) }
456- style = { { '--item-index' : Math . min ( index , 10 ) } as React . CSSProperties }
457484 onClick = { ( ) => onSelect ( note . id ) }
458485 onContextMenu = { e => onContextMenu ( e , note ) }
459486 onKeyDown = { e => {
0 commit comments