@@ -582,10 +582,63 @@ const clearStatusSoon = (ms = 2000) => {
582582 } , ms ) ;
583583} ;
584584
585+ let achievementHintEl = null ;
586+
587+ const ensureAchievementHintEl = ( ) => {
588+ if ( achievementHintEl ) return achievementHintEl ;
589+ achievementHintEl = document . createElement ( "div" ) ;
590+ achievementHintEl . className = "achievement-float-hint" ;
591+ achievementHintEl . setAttribute ( "role" , "tooltip" ) ;
592+ document . body . appendChild ( achievementHintEl ) ;
593+ return achievementHintEl ;
594+ } ;
595+
596+ const showAchievementHint = ( card ) => {
597+ const text = String ( card ?. dataset ?. achievementHint || "" ) . trim ( ) ;
598+ if ( ! text ) return ;
599+ const hint = ensureAchievementHintEl ( ) ;
600+ hint . textContent = text ;
601+ hint . classList . add ( "is-visible" ) ;
602+ hint . style . top = "0px" ;
603+ hint . style . left = "0px" ;
604+
605+ requestAnimationFrame ( ( ) => {
606+ const rect = card . getBoundingClientRect ( ) ;
607+ const hintRect = hint . getBoundingClientRect ( ) ;
608+ const padding = 12 ;
609+ let top = rect . top - hintRect . height - 12 ;
610+ let left = rect . left + rect . width / 2 - hintRect . width / 2 ;
611+
612+ if ( left < padding ) left = padding ;
613+ if ( left + hintRect . width > window . innerWidth - padding ) {
614+ left = window . innerWidth - hintRect . width - padding ;
615+ }
616+ if ( top < padding ) {
617+ top = rect . bottom + 12 ;
618+ }
619+
620+ hint . style . top = `${ top } px` ;
621+ hint . style . left = `${ left } px` ;
622+ } ) ;
623+ } ;
624+
625+ const hideAchievementHint = ( ) => {
626+ if ( ! achievementHintEl ) return ;
627+ achievementHintEl . classList . remove ( "is-visible" ) ;
628+ } ;
629+
585630const renderAchievements = ( payload ) => {
586631 if ( ! achievementGrid ) return ;
587632 const list = Array . isArray ( payload ?. achievements ) ? payload . achievements : [ ] ;
588633 const summary = payload ?. summary || { } ;
634+ const fallbackDescriptions = {
635+ first_record : "Add your first record." ,
636+ five_records : "Add 5 total records." ,
637+ } ;
638+ const fallbackByTitle = {
639+ "first record" : "Add your first record." ,
640+ "tracking momentum" : "Add 5 total records." ,
641+ } ;
589642
590643 if ( achievementStatus ) {
591644 achievementStatus . textContent = list . length
@@ -633,16 +686,28 @@ const renderAchievements = (payload) => {
633686 card . appendChild ( badge ) ;
634687 card . appendChild ( body ) ;
635688
636- const description = String ( item . description || "" ) . trim ( ) ;
689+ const titleKey = String ( item . title || "" ) . trim ( ) . toLowerCase ( ) ;
690+ const description = String (
691+ item . description ||
692+ fallbackDescriptions [ item . key ] ||
693+ fallbackByTitle [ titleKey ] ||
694+ ""
695+ ) . trim ( ) ;
637696 if ( description ) {
638- const hint = document . createElement ( "span" ) ;
639- hint . className = "achievement-hint" ;
640- hint . textContent = description ;
641- card . appendChild ( hint ) ;
697+ card . dataset . achievementHint = description ;
698+ card . tabIndex = 0 ;
699+ card . addEventListener ( "mouseenter" , ( ) => showAchievementHint ( card ) ) ;
700+ card . addEventListener ( "mouseleave" , hideAchievementHint ) ;
701+ card . addEventListener ( "focus" , ( ) => showAchievementHint ( card ) ) ;
702+ card . addEventListener ( "blur" , hideAchievementHint ) ;
642703 }
643704
644705 achievementGrid . appendChild ( card ) ;
645706 } ) ;
707+
708+ achievementGrid . addEventListener ( "scroll" , hideAchievementHint , { passive : true } ) ;
709+ window . addEventListener ( "scroll" , hideAchievementHint , { passive : true } ) ;
710+ window . addEventListener ( "resize" , hideAchievementHint ) ;
646711} ;
647712
648713async function loadAchievements ( ) {
0 commit comments